С++ для начинающих

       

Перегрузка шаблонов функций *


Шаблон функции может быть перегружен. В следующем примере есть три перегруженных объявления для шаблона min():

// определение шаблона класса Array

// (см. раздел 2.4)

template <typename Type>

   class Array( /* ... */ };

// три объявления шаблона функции min()

template <typename Type>

   Type min( const Array<Type>&, int ); // #1

template <typename Type>

   Type min( const Type*, int ); // #2

template <typename Type>

   Type min( Type, Type ); // #3

Следующее определение main() иллюстрирует, как могут вызываться три объявленных таким образом функции:

#include <cmath>

int main()

{

   Array<int> iA(1024);   // конкретизация класса

   int ia[1024];

   // Type == int; min( const Array<int>&, 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 )

min5<int, 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 ; sum<int>( int*, int ); или

// Type == int*; sum<int*>( int*, int ); ??

int ival1 = sum<int>( ia, 1024 );

Как это ни удивительно, такой вызов не приводит к неоднозначности. Шаблон конкретизируется из первого определения, так как выбирается наиболее специализированное

определение. Поэтому для аргумента Type принимается int, а не int*.

Для того чтобы один шаблон был более специализирован, чем другой, оба они должны иметь одни и те же имя и число параметров, а для параметров разных типов, как, скажем, T* и T в предыдущем примере, параметр в одном шаблоне должен быть способен принять более широкое множество фактических аргументов, чем соответствующий параметр в другом. Например, для шаблона sum(Type*, int) вместо первого формального параметра функции разрешается подставлять только фактические аргументы типа “указатель”. В то же время в шаблоне sum(Type, int) первому формальному параметру могут соответствовать фактические аргументы любого типа. Первый шаблон sum(Type*, int) допускает более узкое множество аргументов, чем второй, т.е. он более специализирован, а следовательно, он и конкретизируется при вызове функции.


Содержание раздела