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-значения -
расширение типа или стандартное преобразование -