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

namespace libs_R_us {

int max( int, int );

double max( double, double );

}

// using-объявление

using libs_R_us::max;

char max( char, char );

void func()

{

// все три функции max() являются устоявшими

max( 87, 65 ); // вызывается using libs_R_us::max( int, int )

}

Обратите внимание, что функция-кандидат с несколькими параметрами исключается из числа устоявших, как только выясняется, что один из фактических аргументов не может быть приведен к типу соответствующего формального параметра, пусть даже для всех остальных аргументов такое преобразование существует. В следующем примере функция min(char *, int) исключается из множества устоявших, поскольку нет возможности трансформации типа первого аргумента int в тип соответствующего параметра char *. И это происходит несмотря на то, что второй аргумент точно соответствует второму параметру.

extern double min( double, double );

extern double min( char*, int );

void func()

{

// одна функция-кандидат min( double, double )

min( 87, 65 ); // вызывается min( double, double )

}

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

void print( unsigned int );

void print( char* );

void print( char );

int *ip;

class SmallInt { /* ... */ };

SmallInt si;

int main() {

print( ip ); // ошибка: нет устоявших функций: соответствие не найдено

print( si ); // ошибка: нет устоявших функций: соответствие не найдено

return 0;

}

9.4.3. Наилучшая из устоявших функция

Наилучшей считается та из устоявших функций, формальные параметры которой наиболее точно соответствуют типам фактических аргументов. Для любой такой функции преобразования типов, применяемые к каждому аргументу, ранжируются для определения степени его соответствия параметру. (В разделе 6.2 описаны поддерживаемые преобразования типов.) Наилучшей из устоявших называют функцию, для которой одновременно выполняются два условия:

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

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

Может оказаться так, что для приведения фактического аргумента к типу соответствующего формального параметра нужно выполнить несколько преобразований. Так, в следующем примере

int arr[3];

void putValues(const int *);

int main() {

putValues(arr); // необходимо 2 преобразования

// массив в указатель + преобразование спецификатора

return 0;

}

для приведения аргумента arr от типа “массив из трех int” к типу “указатель на const int” применяется последовательность преобразований:

* Преобразование массива в указатель, которое трансформирует массив из трех int в указатель на int.

* Преобразование спецификатора, которое трансформирует указатель на int в указатель на const int.

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

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

Такая совокупность состоит из нескольких преобразований, применяемых в указанном порядке:

преобразование l-значения -

расширение типа или стандартное преобразование -