10.7. Перегрузка шаблонов функций А
10.7. Перегрузка шаблонов функций А
Шаблон функции может быть перегружен. В следующем примере есть три перегруженных объявления для шаблона min():
// определение шаблона класса Array
// (см. раздел 2.4)
template typename Type
class Array( /* ... */ };
// три объявления шаблона функции min()
template typename Type
Type min( const ArrayType, int ); // #1
template typename Type
Type min( const Type*, int ); // #2
template typename Type
Type min( Type, Type ); // #3
Следующее определение main() иллюстрирует, как могут вызываться три объявленных таким образом функции:
#include cmath
int main()
{
Arrayint iA(1024); // конкретизация класса
int ia[1024];
// Type == int; min( const Arrayint, int )
int ival0 = min( iA, 1024 );
// Type == int; min( const int*, int )
int ival1 = min( ia, 1024 );
// Type == double; min( double, double )
double dval0 = min( sqrt( iA[0] ), sqrt( ia[0] ) );
return 0;
}
Разумеется, тот факт, что три перегруженных шаблона функции успешно объявлены, не означает, что они могут быть также успешно вызваны. Такие шаблоны могут приводить к неоднозначности при вызове конкретизированного шаблона. Например, для следующего определения шаблона min5()
template typename T
int min5( T, T ) { /* ... */ }
функция не конкретизируется по шаблону, если min5() вызывается с аргументами разных типов; при этом процесс вывода заканчивается с ошибкой, поскольку из фактических аргументов функции выводятся два разных типа для T.
int i;
unsigned int ui;
// правильно: для T выведен тип int
min5( 1024, i );
// вывод аргументов шаблона заканчивается с ошибкой:
// для T можно вывести два разных типа
min5 ( i, ui );
Для разрешения второго вызова можно было бы перегрузить min5(), допустив два различных типа аргументов:
template typename T, typename U
int min5( T, U );
При следующем обращении производится конкретизация этого шаблона функции:
// правильно: int min5( int, usigned int )
min5( i, ui );
К сожалению, теперь стал неоднозначным предыдущий вызов:
// ошибка: неоднозначность: две возможных конкретизации
// из min5( T, T ) и min5( T, U )
min5( 1024, i );
Второе объявление min5() допускает наличие у функции аргументов различных типов, но не требует этого. В нашем случае и T, и U типа int. Оба объявления шаблонов могут быть конкретизированы вызовом, в котором два аргумента функции имеют один и тот же тип. Единственный способ указать, какой шаблон более предпочтителен, устранив тем самым неоднозначность, – явно задать его аргументы. (О явном задании аргументов шаблона см. раздел 10.4.) Например:
// правильно: конкретизация из min5( T, U )
min5int, int( 1024, i );
Однако в этом случае мы можем обойтись без перегрузки шаблона функции. Поскольку шаблон min5(T,U) подходит для всех вызовов, для которых подходит min5(T,T), то одного объявления min5(T,U) вполне достаточно, а объявление min5(T,T) можно удалить. Мы уже говорили в главе 9, что, хотя перегрузка допускается, при проектировании таких функций надо быть внимательным и использовать ее только при необходимости. Те же соображения применимы и к определению перегруженных шаблонов.
В некоторых ситуациях неоднозначности при вызове не возникает, хотя по шаблону можно конкретизировать две разных функции. Если имеются следующие два шаблона для функции sum(), то предпочтение будет отдано первому даже тогда, когда конкретизированы могут быть оба:
template typename Type
Type sum( Type*, int );
template typename Type
Type sum( Type, int );
int ia[1024];
// Type == int ; sumint( int*, int ); или
// Type == int*; sumint*( int*, int ); ??
int ival1 = sumint( ia, 1024 );
Как это ни удивительно, такой вызов не приводит к неоднозначности. Шаблон конкретизируется из первого определения, так как выбирается наиболее специализированное определение. Поэтому для аргумента Type принимается int, а не int*.
Для того чтобы один шаблон был более специализирован, чем другой, оба они должны иметь одни и те же имя и число параметров, а для параметров разных типов, как, скажем, T* и T в предыдущем примере, параметр в одном шаблоне должен быть способен принять более широкое множество фактических аргументов, чем соответствующий параметр в другом. Например, для шаблона sum(Type*, int) вместо первого формального параметра функции разрешается подставлять только фактические аргументы типа “указатель”. В то же время в шаблоне sum(Type, int) первому формальному параметру могут соответствовать фактические аргументы любого типа. Первый шаблон sum(Type*, int) допускает более узкое множество аргументов, чем второй, т.е. он более специализирован, а следовательно, он и конкретизируется при вызове функции.