(см. раздел 19.2.6), за исключением того, что ключевое слово
double
теперь заменено шаблонным параметром
T
. Этот шаблонный класс
vector
можно использовать следующим образом:
vector<double> vd; // T — double
vector<int> vi; // T — int
vector<double*> vpd; // T — double*
vector< vector<int> > vvi; // T — vector<int>,
в котором T — int
Можно просто считать, что компилятор генерирует класс конкретного типа (соответствующего шаблонному аргументу), подставляя его вместо шаблонного параметра. Например, когда компилятор видит в программе конструкцию
(см. раздел 19.2.6), используя соответствующее внутреннее имя, подходящее по смыслу конструкции
vector<double>
).
Иногда шаблонный класс называют порождающим типом (type generator). Процесс генерирования типов (классов) с помощью шаблонного класса по заданным шаблонным аргументам называется специализацией (specialization) или конкретизацией шаблона (template instantiation). Например, классы
vector<char>
и
vector<Poly_line*>
называются специализациями класса
vector
. В простых ситуациях, например при работе с классом
vector
, конкретизация не вызывает затруднений. В более общих и запутанных ситуациях конкретизация шаблона очень сильно усложняется. К счастью для пользователей шаблонов, вся эта сложность обрушивается только на разработчика компилятора.
Конкретизация шаблона (генерирование шаблонных специализаций) осуществляется на этапе компиляции или редактирования связей, а не во время выполнения программы.
Естественно, шаблонный класс может иметь функции-члены. Рассмотрим пример.
void fct(vector<string>& v)
{
int n = v.size;
v.push_back("Norah");
// ...
}
При вызове такой функции-члена шаблонного класса компилятор генерирует соответствующую конкретную функцию. Например, когда компилятор видит вызов
соответствует конкретная функция. Иначе говоря, если вам нужна функция с конкретным типом аргумента, компилятор сам напишет ее, основываясь на вашем шаблоне.
Вместо префикса
template<class T>
можно использовать префикс
template <typename T>
. Эти две конструкции означают одно и то же, но некоторые программисты все же предпочитают использовать ключевое слово
typename
, “потому, что оно яснее, и потому, что никто не подумает, что оно запрещает использовать встроенные типы, например тип
int
, в качестве шаблонного аргумента”. Мы считаем, что ключевое слово
class
уже означает “тип”, поэтому никакой разницы между этими конструкциями нет. Кроме того, слово
class
короче.
19.3.2. Обобщенное программирование
Шаблоны — это основа для обобщенного программирования на языке С++. По существу, простейшее определение обобщенного программирования на языке С++ — это программирование с помощью шаблонов. Хотя, конечно, это определение носит слишком упрощенный характер. Не следует давать определения фундаментальных понятий программирования в терминах конструкций языка программирования. Эти конструкции существуют для того, чтобы поддерживать технологии программирования, а не наоборот. Как и большинство широко известных понятий, обобщенное программирование имеет несколько определений. Мы считаем наиболее полезным самое простое из них.
• Обобщенное программирование — это создание кода, работающего с разными типами, заданными в виде аргументов, причем эти типы должны соответствовать специфическим синтаксическим и семантическим требованиям.
Например, элементы вектора должны иметь тип, который можно копировать (с помощью копирующего конструктора и копирующего присваивания). В главах 20-21 будут представлены шаблоны, у которых аргументами являются арифметические операции. Когда мы производим параметризацию класса, мы получаем шаблонный класс (class template), который часто называют также параметризованным типом (parameterized type) или параметризованным классом (parameterized class). Когда мы производим параметризацию функции, мы получаем шаблонную функцию (function template), которую часто называют параметризованной функцией (parameterized function), а иногда алгоритмом (algorithm). По этой причине обобщенное программирование иногда называют алгоритмически ориентированным программированием (algorithm-oriented programming); в этом случае основное внимание при проектировании переносится на алгоритмы, а не на используемые типы.
Поскольку понятие параметризованных типов играет такую важную роль в программировании, мы попытаемся в дальнейшем немного разобраться в этой запутанной терминологии. Это даст нам возможность избежать недоразумений, когда мы встретим знакомые понятия в другом контексте.
Данную форму обобщенного программирования, основанную на явных шаблонных параметрах, часто называют параметрическим полиморфизмом (parametric polymorphism). В противоположность ей полиморфизм, возникающий благодаря иерархии классов и виртуальным функциям, называют специальным полиморфизмом (ad hoc polymorphism), а соответствующий стиль — ориентированным программированием (см. разделы 14.3-14.4). Причина, по которой оба стиля программирования называют полиморфизмом (polymorphism), заключается в том, что каждый из них дает программисту возможность создавать много версий одного и того же понятия с помощью единого интерфейса. Полиморфизм по-гречески означает “много форм”. Таким образом, вы можете манипулировать разными типами с помощью общего интерфейса. В примерах, посвященных классу
Shape
, рассмотренных в главах 16–19, мы буквально работали с разными формами (классами
Text
,
Circle
и
Polygon
) с помощью интерфейса, определенного классом
Shape
. Используя класс
vector
, мы фактически работаем со многими векторами (например,
vector<int>
,
vector<double>
и
vector<Shape*>
) с помощью интерфейса, определенного шаблонным классом