В обоих случаях мы видим, что если формальный параметр-ссылка имеет спецификатор const, то между ним и фактическим аргументом может быть установлено точное соответствие. Следует отметить, что и преобразование l-значения в r-значение, и инициализация ссылки считаются точными соответствиями. В данном примере первый вызов функции приводит к ошибке:
void print( int );
void print( int );
int iobj;
int ri = iobj;
int main() {
print( iobj ); // ошибка: неоднозначность
print( ri ); // ошибка: неоднозначность
print( 86 ); // правильно: вызывается print( int )
return 0;
}
Объект iobj – это аргумент, для которого может быть установлено соответствие с обеими функциями print(), то есть вызов неоднозначен. То же относится и к следующей строке, где ссылка ri обозначает объект, соответствующий обеим функциям print(). С третьим вызовом, однако, все в порядке. Для него print(int) не является устоявшей. Целая константа – это r-значение, так что она не может инициализировать параметр-ссылку. Единственной устоявшей функцией для вызова print(86) является print(int), поэтому она и выбирается при разрешении перегрузки.
Короче говоря, если формальный параметр представляет собой ссылку, то для фактического аргумента точное соответствие устанавливается, если он может инициализировать ссылку, и не устанавливается в противном случае.
Назовите два тривиальных преобразования, допустимых при установлении точного соответствия.
Каков ранг каждого из преобразований аргументов в следующих вызовах функций:
(a) void print( int *, int );
int arr[6];
print( arr, 6 ); // вызов функции
(b) void manip( int, int );
manip( 'a', 'z' ); // вызов функции
(c) int calc( int, int );
double dobj;
double = calc( 55.4, dobj ) // вызов функции
(d) void set( const int * );
int *pi;
set( pi ); // вызов функции
Какие из данных вызовов ошибочны из-за того, что не существует преобразования между типом фактического аргумента и формального параметра:
(a) enum Stat { Fail, Pass };
void test( Stat );
text( 0 ); // вызов функции
(b) void reset( void *);
reset( 0 ); // вызов функции
(c) void set( void * );
int *pi;
set( pi ); // вызов функции
(d) #include list
listint oper();
void print( oper() ); // вызов функции
(e) void print( const int );
int iobj;
print( iobj ); // вызов функции
9.4. Детали разрешения перегрузки функций
В разделе 9.2 мы уже упоминали, что процесс разрешения перегрузки функций состоит из трех шагов:
* Установить множество функций-кандидатов для разрешения данного вызова, а также свойства списка фактических аргументов.
* Отобрать из множества кандидатов устоявшие функции – те, которые могут быть вызваны с данным списком фактических аргументов при учете их числа и типов.
* Выбрать функцию, лучше всего соответствующую вызову, подвергнув ранжированию преобразования, которые необходимо применить к фактическим аргументам, чтобы привести их в соответствие с формальными параметрами устоявшей функции.
Теперь мы готовы к тому, чтобы изучить эти шаги более детально.
9.4.1. Функции-кандидаты
Функцией-кандидатом называется функция, имеющая то же имя, что и вызванная. Кандидаты отыскиваются двумя способами:
объявление функции видимо в точке вызова. В следующем примере
void f();
void f( int );
void f( double, double = 3.4 );
void f( char*, char* );
int main() {
f( 5.6 ); // для разрешения этого вызова есть четыре кандидата
return 0;
}
*
все четыре функции f() удовлетворяют этому условию. Поэтому множество кандидатов содержит четыре элемента; если тип фактического аргумента объявлен внутри некоторого пространства имен, то функции-члены этого пространства, имеющие то же имя, что и вызванная функция, добавляются в множество кандидатов:
namespace NS {
class C { /* ... */ };
void takeC( C );
}
// тип cobj - это класс C, объявленный в пространстве имен NS
NS::C obj;
int main() {