Выбрать главу

Построить множество функций-кандидатов.

* Рассматриваются шаблоны функций с тем же именем, что и вызванная. Если аргументы шаблона выведены из фактических аргументов функции успешно, то в множество функций-кандидатов включается либо конкретизированный шаблон, либо специализация шаблона для выведенных аргументов, если она существует.

Построить множество устоявших функций (см. раздел 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 );

}

Правило 3b говорит, что, поскольку вызов неоднозначен, следует рассматривать только обычные функции. Ни одна из них не считается наилучшей из устоявших, так как преобразования типов фактических аргументов одинаково плохи: в обоих случаях для установления соответствия требуется стандартная трансформация. Таким образом, вызов неоднозначен, и компилятор сообщает об ошибке.

Упражнение 10.11

Вернемся к представленному ранее примеру:

template class Type

Type max( Type, Type ) { ... }

double max( double, double );

int main() {

int ival;

double dval;

float fd;

max( 0, ival );

max( 0.25, dval );

max( 0, fd );

}

Добавим в множество объявлений в глобальной области видимости следующую специализацию шаблона функции:

template char maxchar* char, char ) { ... }

Составьте список кандидатов и устоявших функций для каждого вызова max() внутри main().

Предположим, что в main() добавлен следующий вызов:

int main() {

// ...

max( 0, 'j' );

}

В пользу какой функции он будет разрешен? Почему?

Упражнение 10.12

Предположим, что есть следующее множество определений и специализаций шаблонов, а также объявления переменных и функций: