void call_instantiation() {
// ошибка: для этого вызова нет функции-кандидата
min( ai[0], ss );
}
// обычная функция
int min( int a1, int a2 ) {
minint( a1, a2 );
}
int main() {
call_instantiation() {
// вызывается обычная функция
min( ai[0], ss );
}
Для вызова min(ai[0],ss) из call_instantiation нет ни одной функции-кандидата. Попытка сгенерировать ее из шаблона min() провалится, поскольку для аргумента шаблона Type из фактических аргументов функции выводятся два разных значения. Следовательно, такой вызов ошибочен. Однако при обращении к min(ai[0],ss) внутри main() видимо объявление обычной функции min(int, int). Тип первого фактического аргумента этой функции точно соответствует типу формального параметра, а второй аргумент может быть преобразован в тип формального параметра с помощью расширения типа. Поскольку для второго вызова устояла только данная функция, то она и вызывается.
Разобравшись с разрешением перегрузки функций, конкретизированных из шаблонов, специализацией шаблонов функций и обычных функций с тем же именем, подытожим все, что мы об этом рассказали:
Построить множество функций-кандидатов.
* Рассматриваются шаблоны функций с тем же именем, что и вызванная. Если аргументы шаблона выведены из фактических аргументов функции успешно, то в множество функций-кандидатов включается либо конкретизированный шаблон, либо специализация шаблона для выведенных аргументов, если она существует.
Построить множество устоявших функций (см. раздел 9.3).
* В множестве функций-кандидатов остаются только функции, которые можно вызвать с данными фактическими аргументами.
* Ранжировать преобразования типов (см. раздел 9.3). Если есть только одна функция, вызвать именно ее.
* Если вызов неоднозначен, удалить из множества устоявших функции, конкретизированные из шаблонов.
*
* Разрешить перегрузку, рассматривая среди всех устоявших только обычные функции (см. раздел 9.3). Если есть только одна функция, вызвать именно ее.
* В противном случае вызов неоднозначен.
*
Проиллюстрируем эти шаги на примере. Предположим, есть два объявления – шаблона функции и обычной функции. Оба принимают аргументы типа double:
template class Type
Type max( Type, Type ) { ... }
// обычная функция
double max( double, double );
А вот три вызова max(). Можете ли вы сказать, какая функция будет вызвана в каждом случае?
int main() {
int ival;
double dval;
float fd;
// ival, dval и fd присваиваются значения
max( 0, ival );
max( 0.25, dval );
max( 0, fd );
}
Рассмотрим последовательно все три вызова:
* max(0,ival). Оба аргумента имеют тип int. Для вызова есть два кандидата: конкретизированная из шаблона функция max(int, int) и обычная функция max(double, double). Конкретизированная функция точно соответствует фактическим аргументам, поэтому она и вызывается;
* max(0.25,double). Оба аргумента имеют тип double. Для вызова есть два кандидата: конкретизированная из шаблона max(double, double) и обычная max(double, double). Вызов неоднозначен, поскольку точно соответствует обеим функциям. Правило 3b говорит, что в таком случае выбирается обычная функция;.
* max(0,fd). Аргументы имеют тип int и float соответственно. Для вызова существует только один кандидат: обычная функция max(double, double). Вывод аргументов шаблона заканчивается неудачей, так как значения типа Type, выведенные из разных фактических аргументов функции, различны. Поэтому в множество кандидатов конкретизированная из шаблона функция не попадает. Обычная же функция устояла, поскольку существуют преобразования типов фактических аргументов в типы формальных параметров; она и выбирается. Если бы обычная функция не была объявлена, вызов закончился бы ошибкой.
А если бы мы определили еще одну обычную функцию для max()? Например:
template class T T max( T, T ) { ... }
// две обычные функции
char max( char, char );
double max( double, double );
Будет ли в таком случае третий вызов разрешен по-другому? Да.
int main() {
float fd;
// в пользу какой функции разрешается вызов?
max( 0, fd );
}