Конкретизация шаблона функции
Шаблон функции описывает, как следует строить конкретные функции, если задано множество фактических типов или значений. Процесс конструирования называется конкретизацией шаблона. Выполняется он неявно, как побочный эффект вызова или взятия адреса шаблона функции. Например, в следующей программе min() конкретизируется дважды: один раз для массива из пяти элементов типа int, а другой – для массива из шести элементов типа 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 };
double da[6] = { 10.2, 7.1, 14.5, 3.2, 25.0, 16.8 };
#include <iostream>
int main()
{
// конкретизация min() для массива из 5 элементов типа int
// подставляется Type => int, size => 5
int i = min( ia );
if ( i != 3 )
cout << "??oops: integer min() failed\n";
else cout << "!!ok: integer min() worked\n";
// конкретизация min() для массива из 6 элементов типа double
// подставляется Type => double, size => 6
double d = min( da );
if ( d != 3.2 )
cout << "??oops: double min() failed\n";
else cout << "!!ok: double min() worked\n";
return 0;
}
Вызов
int i = min( ia );
приводит к конкретизации следующего экземпляра функции min(), в котором Type заменено на int, а size на 5:
int min( int (&r_array)[5] )
{
int min_val = r_array[0];
for ( int i = 1; i < 5; ++i )
if ( r_array[i] < min_val )
min_val = r_array[i];
return min_val;
}
Аналогично вызов
double d = min( da );
конкретизирует экземпляр min(), в котором Type заменено на double, а size на 6:
В качестве формальных параметров шаблона функции используются параметр-тип и параметр-константа. Для определения фактического типа и значения константы, которые надо подставить в шаблон, исследуются фактические аргументы, переданные при вызове функции. В нашем примере для идентификации аргументов шаблона при конкретизации используются тип ia (массив из пяти int) и da (массив из шести double). Процесс определения типов и значений аргументов шаблона по известным фактическим аргументам функции называется выведением (deduction) аргументов шаблона. (В следующем разделе мы расскажем об этом подробнее. А в разделе 10.4 речь пойдет о возможности явного задания аргументов.)
Шаблон конкретизируется либо при вызове, либо при взятии адреса функции. В следующем примере указатель pf инициализируется адресом конкретизированного экземпляра шаблона. Его аргументы определяются путем исследования типа параметра функции, на которую указывает pf:
template <typename Type, int size>
Type min( Type (&p_array)[size] ) { /* ... */ }
// pf указывает на int min( int (&)[10] )
int (*pf)(int (&)[10]) = &min;
Тип pf – это указатель на функцию с параметром типа int(&)[10], который определяет тип аргумента шаблона Type и значение аргумента шаблона size при конкретизации min(). Аргумент шаблона Type будет иметь тип int, а значением аргумента шаблона size будет 10. Конкретизированная функция представляется как min(int(&)[10]), и указатель pf адресует именно ее.
Когда берется адрес шаблона функции, контекст должен быть таким, чтобы можно было однозначно определить типы и значения аргументов шаблона. Если сделать это не удается, компилятор выдает сообщение об ошибке:
template <typename Type, int size>
Type min( Type (&r_array)[size] ) { /* ... */ }
typedef int (&rai)[10];
typedef double (&rad)[20];
void func( int (*)(rai) );
void func( double (*)(rad) );
int main() {
// ошибка: как конкретизировать min()?
func( &min );
}
Функция func() перегружена и тип ее параметра не позволяет однозначно определить ни аргумент шаблона Type, ни значение аргумента шаблона size. Результатом конкретизации вызова func() может быть любая из следующих функций:
min( int (*)(int(&)[10]) )
min( double (*)(double(&)[20]) )
Поскольку однозначно определить аргументы функции func() нельзя, взятие адреса конкретизированного шаблона в таком контексте приводит к ошибке компиляции.
Этого можно избежать, если использовать явное приведение типов для указания типа аргумента:
int main() {
// правильно: с помощью явного приведения указывается тип аргумента
func( static_cast< double(*)(rad) >(&min) );
}
Лучше, однако, применять явное задание аргументов шаблона, как будет показано в разделе 10.4.