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

class B;

void takeB( B );

B giveB();

int main() {

takeB( giveB() ); // ошибка: параметр должен быть типа const B

return 0;

}

*

Вызов функции takeB() – ошибка. Фактический аргумент – это возвращаемое значение, т.е. временная переменная, которая не может быть использована для инициализации ссылки без спецификатора const.

В обоих случаях мы видим, что если формальный параметр-ссылка имеет спецификатор 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), поэтому она и выбирается при разрешении перегрузки.

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

Упражнение 9.6

Назовите два тривиальных преобразования, допустимых при установлении точного соответствия.

Упражнение 9.7

Каков ранг каждого из преобразований аргументов в следующих вызовах функций:

(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 ); // вызов функции

Упражнение 9.8

Какие из данных вызовов ошибочны из-за того, что не существует преобразования между типом фактического аргумента и формального параметра:

(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() удовлетворяют этому условию. Поэтому множество кандидатов содержит четыре элемента; если тип фактического аргумента объявлен внутри некоторого пространства имен, то функции-члены этого пространства, имеющие то же имя, что и вызванная функция, добавляются в множество кандидатов: