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 );

}

Перейти на страницу:

Похожие книги