При таком разборе компилятор должен отличать выражения-типы от тех, которые таковыми не являются; выявить это не всегда возможно. Например, если компилятор встречает в определении шаблона выражение Parm::name и если Parm – это параметр-тип, представляющий класс, то следует ли считать, что name представляет член-тип класса Parm?
template class Parm, class U
Parm minus( Parm* array, U value )
{
Parm::name * p; // это объявление указателя или умножение?
// На самом деле умножение
}
Компилятор не знает, является ли name типом, поскольку определение класса, представленного параметром Parm, недоступно до момента конкретизации шаблона. Чтобы такое определение шаблона можно было разобрать, пользователь должен подсказать компилятору, какие выражения включают типы. Для этого служит ключевое слово typename. Например, если мы хотим, чтобы выражение Parm::name в шаблоне функции minus() было именем типа и, следовательно, вся строка трактовалась как объявление указателя, то нужно модифицировать текст следующим образом:
template class Parm, class U
Parm minus( Parm* array, U value )
{
typename Parm::name * p; // теперь это объявление указателя
}
Ключевое слово typename используется также в списке параметров шаблона для указания того, что параметр является типом.
Шаблон функции можно объявлять как inline или extern – как и обычную функцию. Спецификатор помещается после списка параметров, а не перед словом template.
// правильно: спецификатор после списка параметров
template typename Type
inline
Type min( Type, Type );
// ошибка: спецификатор inline не на месте
inline
template typename Type
Type min( ArrayType, int );
Определите, какие из данных определений шаблонов функций неправильны. Исправьте ошибки.
(a) template class T, U, class V
void foo( T, U, V );
(b) template class T
T foo( int *T );
(c) template class T1, typename T2, class T3
T1 foo( T2, T3 );
(d) inline template typename T
T foo( T, unsigned int* );
(e) template class myT, class myT
void foo( myT, myT );
(f) template class T
foo( T, T );
(g) typedef char Ctype;
template class Ctype
Ctype foo( Ctype a, Ctype b );
Какие из повторных объявлений шаблонов ошибочны? Почему?
(a) template class Type
Type bar( Type, Type );
template class Type
Type bar( Type, Type );
(b) template class T1, class T2
void bar( T1, T2 );
template typename C1, typename C2
void bar( C1, C2 );
Перепишите функцию putValues() из раздела 7.3.3 в виде шаблона. Параметризуйте его так, чтобы было два параметра шаблона (для типа элементов массива и для размера массива) и один параметр функции, являющийся ссылкой на массив. Напишите определение шаблона функции.
10.2. Конкретизация шаблона функции
Шаблон функции описывает, как следует строить конкретные функции, если задано множество фактических типов или значений. Процесс конструирования называется конкретизацией шаблона. Выполняется он неявно, как побочный эффект вызова или взятия адреса шаблона функции. Например, в следующей программе min() конкретизируется дважды: один раз для массива из пяти элементов типа int, а другой – для массива из шести элементов типа double:
массива из шести элементов типа double:
// определение шаблона функции min()
// с параметром-типом Type и параметром-константой size
template typename Type, int size
Type min( Type (r_array)[size] )
{
Type min_val = r_array[0];
for ( int i = 1; i size; ++i )
if ( r_array[i] min_val )
min_val = r_array[i];
return min_val;
}
// size не задан -- ok
// size = число элементов в списке инициализации
int ia[] = { 10, 7, 14, 3, 25 };