Процесс подстановки типов и значений вместо параметров называется конкретизацией шаблона. (Подробнее мы остановимся на этом в следующем разделе.)
Список параметров нашей функции min() может показаться чересчур коротким. Как было сказано в разделе 7.3, когда параметром является массив, передается указатель на его первый элемент, первая же размерность фактического аргумента-массива внутри определения функции неизвестна. Чтобы обойти эту трудность, мы объявили первый параметр min() как ссылку на массив, а второй – как его размер. Недостаток подобного подхода в том, что при использовании шаблона с массивами одного и того же типа int, но разных размеров генерируются (или конкретизируются) различные экземпляры функции min().
Имя параметра разрешено употреблять внутри объявления или определения шаблона. Параметр-тип служит спецификатором типа; его можно использовать точно так же, как спецификатор любого встроенного или пользовательского типа, например в объявлении переменных или в операциях приведения типов. Параметр-константа применяется как константное значение – там, где требуются константные выражения, например для задания размера в объявлении массива или в качестве начального значения элемента перечисления.
// size определяет размер параметра-массива и инициализирует
// переменную типа const int
template class Type, int size
Type min( const Type (r_array)[size] )
{
const int loc_size = size;
Type loc_array[loc_size];
// ...
}
Если в глобальной области видимости объявлен объект, функция или тип с тем же именем, что у параметра шаблона, то глобальное имя оказывается скрытым. В следующем примере тип переменной tmp не double, а тот, что у параметра шаблона Type:
typedef double Type;
template class Type
Type min( Type a, Type b )
{
// tmp имеет тот же тип, что параметр шаблона Type, а не заданный
// глобальным typedef
Type tm = a b ? a : b;
return tmp;
}
Объект или тип, объявленные внутри определения шаблона функции, не могут иметь то же имя, что и какой-то из параметров:
template class Type
Type min( Type a, Type b )
{
// ошибка: повторное объявление имени Type, совпадающего с именем
// параметра шаблона
typedef double Type;
Type tmp = a b ? a : b;
return tmp;
}
Имя параметра-типа шаблона можно использовать для задания типа возвращаемого значения:
// правильно: T1 представляет тип значения, возвращаемого min(),
// а T2 и T3 – параметры-типы этой функции
template class T1, class T2, class T3
T1 min( T2, T3 );
В одном списке параметров некоторое имя разрешается употреблять только один раз. Например, следующее определение будет помечено как ошибка компиляции:
// ошибка: неправильное повторное использование имени параметра Type
template class Type, class Type
Type min( Type, Type );
Однако одно и то же имя можно многократно применять внутри объявления или определения шаблона:
// правильно: повторное использование имени Type внутри шаблона
template class Type
Type min( Type, Type );
template class Type
Type max( Type, Type );
Имена параметров в объявлении и определении не обязаны совпадать. Так, все три объявления min() относятся к одному и тому же шаблону функции:
// все три объявления min() относятся к одному и тому же шаблону функции
// опережающие объявления шаблона
template class T T min( T, T );
template class U U min( U, U );
// фактическое определение шаблона
template class Type
Type min( Type a, Type b ) { /* ... */ }
Количество появлений одного и того же параметра шаблона в списке параметров функции не ограничено. В следующем примере Type используется для представления двух разных параметров:
#include vector
// правильно: Type используется неоднократно в списке параметров шаблона
template class Type
Type sum( const vectorType , Type );
Если шаблон функции имеет несколько параметров-типов, то каждому из них должно предшествовать ключевое слово class или typename:
// правильно: ключевые слова typename и class могут перемежаться
template typename T, class U
T minus( T*, U );
// ошибка: должно быть typename T, class U или
// typename T, typename U
template typename T, U
T sum( T*, U );
В списке параметров шаблона функции ключевые слова typename и class имеют одинаковый смысл и, следовательно, взаимозаменяемы. Любое из них может использоваться для объявления разных параметров-типов шаблона в одном и том же списке (как было продемонстрировано на примере шаблона функции minus()). Для обозначения параметра-типа более естественно, на первый взгляд, употреблять ключевое слово typename, а не class, ведь оно ясно указывает, что за ним следует имя типа. Однако это слово было добавлено в язык лишь недавно, как часть стандарта C++, поэтому в старых программах вы скорее всего встретите слово class. (Не говоря уже о том, что class короче, чем typename, а человек по природе своей ленив.)