Template class t c что это
Шаблоны
Шаблон класса
Шаблоны позволяют определить конструкции (функции, классы), которые используют определенные типы, но на момент написания кода точно не известно, что это будут за типы. Иными словами, шаблоны позволяют определить универсальные конструкции, которые не зависят от определенного типа.
Шаблон класса (class template) позволяет задать тип для объектов, используемых в классе. Но прежде чем перейти к определению шаблона класса, рассмотрим проблему, с которой мы можем столкнуться и которую позволяют решить шаблоны.
Хотя данный пример работает, но по сути мы получаем два идентичных класса, которые отличаются только типом id. Шаблоны класса позволяют уменьшить повторяемость кода, задав для класса универсальный тип. Изменим код, применив шаблоны:
Параметр шаблона представляет произвольный идентификатор, в качестве которого, как правило, применяюся заглавные буквы, например, T. Но это необязательно. То есть в данном случае параметр T будет представлять некоторый тип, который становится известным во время компиляции. Это может быть и тип int, и double, и string, и любой другой тип. И теперь идентификатор счета будет представлять тип, который передается через параметр T.
Используем этот класс:
Во втором случае применяется тип int, поэтому в конструктор передается число:
Также можно применять сразу несколько параметров. Например, необходимо определить класс банковского перевода:
При использовании шаблона в этом случае надо указать два типа:
Введение в магию шаблонов
Зачем?
Мы используем шаблоны для красоты. Каждый С++ разработчик знает, что такое красота, красота — это когда код компактный, понятный и быстрый.
Мета-магия и неявные интерфейсы
Что такое метопрограмма? Метопрограмма — это программа, результатом работы которой будет другая программа. Для С++ выполнением метапрограмм занимается компилятор, а результатом является бинарный файл.
Именно для написания метапрограмм используются шаблоны.
Чем еще отличается полиморфизм шаблонов от полиморфизма виртуальных функций? Если класс обладает явным интрерфейсом, который мы определили в объявлении класса, то далее в программе объекты этого типа могут использоваться в соответствии с этим самым интерфесом. А вот для шаблонов мы используем неявные интерфейсы, т.е. использованием объекта типа мы определяем неявный интерфейс типа, который выведет компилятор при построении метапрограммы.
Первые заклинания: волшебная дубина
Конкретизируем наш шаблон и посмотрим, какие типы мы получили для различных параметров шаблона:
В выводе программы видно, что типы конкретизаций шаблона разные даже для эквивалентных типов — unsigned char & char. При этом они идентичны для char & CHAR, т.к. typedef не создает тип, а лишь дает ему другое имя. Идентичны они и для выражений 1 и 2-1, т.к. компилятор вычисляет выражения и вместо 2-1 использует 1.
Отсюда и вытекает, что мы не можем использовать для шаблонов раздельную компиляцию без дополнительных проблем:
Вообще, в стандарте С++ для этого есть ключевое слово export, однако эта фича слишком труднореализуема и отсутствует в большинстве компиляторов. Есть компиляторы, которые ее поддерживают, но не советую ее использовать в переносимом коде.
Кроме классов существуют и шаблоны функций:
Если компилятор может вывести тип параметра шаблона из типа параметров — он так и поступит, при этом нам не нужно указывать его в коде. Если нет, то мы можем определить разрешающую функцию:
Она не несет никаких накладных расходов.
Специализация — это новый уровень
Обычно используя шаблоны мы хотим написать универсальный код, однако в некоторых случаях мы можем проиграть в производительности. Для решения проблемы существует специальное заклятие — специализация шаблона. Специализация — это повторное определение шаблона с конкретным типом либо классом типов:
Компилятор сам выберет наиболее точно подходящую специализацию, в примере это класс типов “указатель на тип”.
Зловещая магия: рекурсия
Специализации и тот факт, что мы можем использовать шаблоны в шаблонах, дает дам одну очень интересную возможность — рекурсия времени компиляции.
Самый простой и популярный пример — вычисление какого-либо ряда или полинома, скажем, сумма ряда натуральных чисел:
Смотрим… Работает! Круто? Увеличим количество итераций до 500:
Теперь компиляция занимает больше времени, при этом время выполнения программы — константа! Чудеса!
Не делай козу если хотел грозу
Тут есть пара моментов.
Максимальная глубина рекурсии по умолчанию ограничена реализацией, для нового gcc это 900, для старых версий он меньше. Параметр
снимает это ограничение.
Второй подводный камень — не ждите отчетов об ошибках. Меняем сумму на факториал:
Получаем некорректный результат, и ни одного предупреждения.
Третий момент, очевидный: мы можем создать слишком много почти одинаковых конкретизаций шаблона и вместо прироста производительности получить прирост бинарного кода.
Мощные заклинания древних
А можно ли совместить магию наследования с шаблонной магией?
Древние используют для этого заклинание CRTP. Идея проста: применить не виртуальное наследование и обеспечить полиморфное поведение с помощью явного приведения типа наследника к типу родителя. Давайте рассмотрим пример использования:
Мы получаем наследуемые inline методы с полиморфным поведением! Кто скажет что это не круто — мой враг навсегда.
Древние также советуют добавлять в конструктор родителя что-то типа того:
Чтобы демоны, разбуженные мощным заклинанием, не смогли причинить вред вызвавшему их магу.
Есть еще много тайных техник, древних и не очень. Надеюсь на не скорую встречу /*в аду*/, и да прибудет с вами мощь древних.
Лекция 8. Шаблоны
1 шаблоны функций и классов
Понятие шаблона
Шаблоны обеспечивают непосредственную поддержку обобщенного программирования (т.е. c использованием типов в качестве параметров)
Шаблон зависит только от тех свойств параметра-типа, которые он явно использует
Существуют шаблоны функций и классов
Инстанцирование
Процесс порождения функции или класса из шаблона называется инстанцированием
Процесс генерации объявления класса по шаблону класса и аргументу шаблона
Версия шаблона для конкретного аргумента шаблона называется специализацией
1.1 Шаблоны функций
Для создания шаблона используется ключевое слово template. Также указывается пока неопределенный тип T.
Рассмотрим пример шаблонной функции swap для обмена значений двух переменных
Для получения функции проведем инстанцирование
Еще один пример: функция сортировки
Пример шаблона с целочисленным параметром
Примеры использования шаблонов
1.2 Шаблоны классов
Аналогично функциям можно создавать шаблоны классов. Рассмотрим пример стека:
Воспользоваться шаблоном класса можно так
Описание конструктора и деструктора шаблонного класса
Описание методов push и pop
Описание методов определения размера стека
Примеры инстанцирования
Рассмотрим примеры использования шаблона стека
1.3 Параметры шаблонов
Параметры шаблона
У шаблонов могут быть параметры различных типов
Инстанцирование выполняется с указанием значения параметра
В шаблонах допускается использование различных видов параметров
Если в шаблоне класса или функции необходимо использовать один и тот же шаблон, но с разными параметрами, то используются параметры-шаблоны. Например:
Перегрузка шаблонов
Специализация шаблонов
Явное инстанцирование
Явное инстанцирование используется
2 Специализация шаблонов
Для чего нужна специализация шаблонов? Для того чтобы задать шаблон для отдельного значения параметра (типа или значения).
В приведённом примере создаётся шаблон функции сравнения двух элементов одного типа. Так можно сравнивать любые числа, но не строки.
Для строк создаётся специализация.
2.1 Класс Bag
Шаблон класса Bag
В следующем примере приводится шаблон класса Bag, который является
динамическим контейнером элементов и его специализация,
позволяющая задавать элементы не по значению, а по указателю.
2.2 Пример с наследованием
Мы можем использовать специализацию при наследовании.
Рассмотрим следующую ситуацию. Допустим мы хотим создать класс, услуги которого базируются на сходных по назначению, но отличных в их реализации классах BaseA и BaseB. Если оба базовых класса обладают схожим интерфейсом, то логично оформить наш класс в виде шаблона:
если разработчику необходима информация о базовых классах объектов, основанных на шаблоне Derived. Как ее получить?
Следующий метод основан на введении вспомогательного шаблона класса (или структуры) BaseClassInstance, не содержащего ничего, кроме статической константы типа BaseClass, и специализированного для разных фактических типов базовых классов.
Описываем шаблон класса-наследника, в который помещается метод GetBaseClass
Вот так можно воспользоваться созданными шаблонами:
BestProg
Содержание
Поиск на других ресурсах:
1. Что называется шаблоном класса? Что такое шаблон класса?
Часто, при разработке классов для разных типов данных, программисту приходится писать программный код для каждого типа в отдельности. Методы и операции над данными разных типов могут содержать один и тот же повторяемый код. Во избежание повторяемости написания кода для разных типов данных, в языке C++ используются так называемые шаблоны (templates).
Фактически, объявление шаблона класса есть только описанием. Создание реального класса с заданным типом данных осуществляется компилятором в момент компиляции, когда объявляется объект класса.
2. Какая общая форма объявления шаблона класса и объекта шаблонного класса, которые не содержат аргументов? Ключевое слово template
В простейшем случае общая форма объявления шаблона класса без аргументов имеет следующий вид:
Общая форма объявления объекта шаблонного класса имеет следующий вид:
3. Какие преимущества дает использование шаблонов классов?
Объявление шаблона класса дает следующие преимущества:
4. Пример объявления шаблона класса, который содержит методы обработки числа, тип которого может быть целым или вещественным
Пусть нужно объявить шаблон класса, который будет обрабатывать некоторое число. Число может быть любого типа, который позволяет выполнять над ним арифметические операции.
В примере объявляется шаблон класса, содержащий методы, которые выполняют следующие операции над некоторым числом:
Объявление шаблона имеет вид
Использование шаблона класса MyNumber в другом программном коде
5. Общая форма объявления шаблона класса, принимающего аргументы
Бывают случаи, когда в шаблоне класса нужно использовать некоторые аргументы. Эти аргументы могут использоваться методами, которые описываются в шаблоне класса.
Общая форма шаблона класса, содержащего аргументы, следующая:
Общая форма объявления объекта шаблонного класса, содержащего один аргумент:
6. Пример использования шаблона класса, принимающего два аргумента
Шаблон класса получает два целых числа:
Эти числа используются в методах для выполнения операций над массивом. Шаблон класса содержит следующие данные и методы:
Текст шаблона класса следующий:
Использование шаблона в некотором другом программном коде (функции, методе)
Урок №175. Шаблоны классов
Обновл. 15 Сен 2021 |
На предыдущих уроках мы узнали, как с помощью шаблонов функций сделать одну версию функции, которая будет работать с разными типами данных. Хотя это значительный шаг на пути к обобщенному программированию, это не решает всех наших проблем. Рассмотрим пример такой проблемы и то, как шаблоны могут нам помочь в её решении.
Шаблоны и контейнерные классы
На уроке о контейнерных классах мы узнали то, как, используя композицию, реализовать классы, содержащие несколько объектов определенного типа данных. В качестве примера мы использовали класс ArrayInt:
Хотя этот класс обеспечивает простой способ создания массива целочисленных значений, но что, если нам нужно будет работать со значениями типа double? Используя традиционные методы программирования мы создали бы новый класс ArrayDouble для работы со значениями типа double:
Хотя кода много, но классы почти идентичны, меняется только тип данных! Как вы уже могли бы догадаться, это идеальный случай для использования шаблонов. Создание шаблона класса аналогично созданию шаблона функции. Например, создадим шаблон класса Array:
Вот пример использования шаблона класса Array:
9 9.5
8 8.5
7 7.5
6 6.5
5 5.5
4 4.5
3 3.5
2 2.5
1 1.5
0 0.5
Шаблоны классов работают точно так же, как и шаблоны функций: компилятор копирует шаблон класса, заменяя типы параметров шаблона класса на фактические (передаваемые) типы данных, а затем компилирует эту копию. Если у вас есть шаблон класса, но вы его не используете, то компилятор не будет его даже компилировать.
Шаблоны классов идеально подходят для реализации контейнерных классов, так как очень часто таким классам приходится работать с разными типами данных, а шаблоны позволяют это организовать в минимальном количестве кода. Хотя синтаксис несколько уродлив, и сообщения об ошибках иногда могут быть «объемными», шаблоны классов действительно являются одной из лучших и наиболее полезных конструкций языка C++.
Шаблоны классов в Стандартной библиотеке С++
Шаблоны классов и Заголовочные файлы
Шаблон не является ни классом, ни функцией — это трафарет, используемый для создания классов или функций. Таким образом, шаблоны работают не так, как обычные функции или классы. В большинстве случаев это не является проблемой, но на практике случаются разные ситуации.
Вышеприведенная программа скомпилируется, но вызовет следующую ошибку линкера:
unresolved external symbol «public: int __thiscall Array::getLength(void)» (?GetLength@?$Array@H@@QAEHXZ)
Почему так? Сейчас разберемся.
Эту проблему можно решить несколькими способами.
Самый простой вариант — поместить код из Array.cpp в Array.h ниже класса. Таким образом, когда мы будем подключать Array.h, весь код шаблона класса (полное объявление и определение как класса, так и его методов) будет находиться в одном месте. Плюс этого способа — простота. Минус — если шаблон класса используется во многих местах, то мы получим много локальных копий шаблона класса, что увеличит время компиляции и линкинга файлов (линкер должен будет удалить дублирование определений класса и методов, дабы исполняемый файл не был «слишком раздутым»). Рекомендуется использовать это решение до тех пор, пока время компиляции или линкинга не является проблемой.
Если вы считаете, что размещение кода из Array.cpp в Array.h сделает Array.h слишком большим/беспорядочным, то альтернативой будет переименование Array.cpp в Array.inl (.inl от англ. «inline» = «встроенный»), а затем подключение Array.inl из нижней части файла Array.h. Это даст тот же результат, что и размещение всего кода в заголовочном файле, но таким образом код получится немного чище.
Еще один альтернативный вариант — использовать подход трех файлов:
Определение шаблона класса хранится в заголовочном файле.
Затем добавляем третий файл, который содержит все необходимые нам экземпляры шаблона класса.