Функции-кандидаты
Функцией-кандидатом называется функция с тем же именем, что и вызванная. Предположим, что имеется такой вызов:
SmallInt si(15);
add( si, 566 );
Функция-кандидат должна иметь имя add. Какие из объявлений add() принимаются во внимание? Те, которые видимы в точке вызова.
Например, обе функции add(), объявленные в глобальной области видимости, будут кандидатами для следующего вызова:
const matrix& add( const matrix &, int );
double add( double, double );
int main() {
SmallInt si(15);
add( si, 566 );
// ...
}
Рассмотрение функций, чьи объявления видны в точке вызова, производится не только для вызовов с аргументами типа класса. Однако для них поиск объявлений проводится еще в двух областях видимости:
- если фактический аргумент – это объект типа класса, указатель или ссылка на тип класса либо указатель на член класса и этот тип объявлен в пользовательском пространстве имен, то к множеству функций-кандидатов добавляются функции, объявленные в этом же пространстве и имеющие то же имя, что и вызванная:
- если фактический аргумент – это объект типа класса, указатель или ссылка на класс либо указатель на член класса и у этого класса есть друзья, имеющие то же имя, что и вызванная функция, то они добавляются к множеству функций-кандидатов:
- глобальные функции:
- функция из пространства имен:
- функция-друг:
- функций, видимых в точке вызова;
- функций, объявленных в тех пространствах имен, где определен тип класса или любой из его базовых;
- функций, являющихся друзьями этого класса или любого из его базовых.
namespace NS {
class SmallInt { /* ... */ };
class String { /* ... */ };
String add( const String &, const String & );
}
int main() {
// si имеет тип class SmallInt:
// класс объявлен в пространстве имен NS
NS::SmallInt si(15);
add( si, 566 ); // NS::add() - функция-кандидат
return 0;
}
Аргумент si имеет тип SmallInt, т.е. тип класса, объявленного в пространстве имен NS. Поэтому к множеству функций-кандидатов добавляется add(const String &, const String &), объявленная в этом пространстве имен;
namespace NS {
class SmallInt {
friend SmallInt add( SmallInt, int ) { /* ... */ }
};
}
int main() {
NS::SmallInt si(15);
add( si, 566 ); // функция-друг add() - кандидат
return 0;
}
Аргумент функции si имеет тип SmallInt. Функция-друг класса SmallInt add(SmallInt, int) – член пространства имен NS, хотя непосредственно в этом пространстве она не объявлена. При обычном поиске в NS функция-друг не будет найдена. Однако при вызове add() с аргументом типа класса SmallInt принимаются во внимание и добавляются к множеству кандидатов также друзья этого класса, объявленные в списке его членов.
Таким образом, если в списке фактических аргументов функции есть объект, указатель или ссылка на класс, а также указатели на члены класса, то множество функций-кандидатов состоит из множества функций, видимых в точке вызова, или объявленных в том же пространстве имен, где определен тип класса, или объявленных друзьями этого класса.
Рассмотрим следующий пример:
namespace NS {
class SmallInt {
friend SmallInt add( SmallInt, int ) { /* ... */ }
};
class String { /* ... */ };
String add( const String &, const String & );
}
const matrix& add( const matrix &, int );
double add( double, double );
int main() {
// si имеет тип class SmallInt:
// класс объявлен в пространстве имен NS
NS::SmallInt si(15);
add( si, 566 ); // вызывается функция-друг
return 0;
}
Здесь кандидатами являются:
const matrix& add( const matrix &, int )
double add( double, double )
NS::add( const String &, const String & )
NS::add( SmallInt, int )
При разрешении перегрузки выбирается функция-друг класса SmallInt NS::add( SmallInt, int ) как наилучшая из устоявших: оба фактических аргумента точно соответствуют заданным формальным параметрам.
Разумеется, вызванная функция может быть несколько аргументов типа класса, указателя или ссылки на класс либо указателя на член класса. Допускаются разные типы классов для каждого из таких аргументов. Поиск функций-кандидатов для них ведется в пространстве имен, где определен класс, и среди функций-друзей класса. Поэтому результирующее множество кандидатов для вызова функции с такими аргументами содержит функции из разных пространств имен и функции-друзья, объявленные в разных классах.
namespace NS {
class ZooAnimal {
friend void display( const ZooAnimal& );
};
}
// базовый класс Bear объявлен в пространстве имен NS
class Bear : public NS::ZooAnimal { };
int main() {
Bear baloo;
display( baloo );
return 0;
}
Аргумент baloo функции display() имеет тип Bear. В его базовом классе ZooAnimal функция display() объявлена другом, поэтому она является членом пространства имен NS, хотя явно в нем не объявлена. При обычном просмотре NS она не была бы найдена. Однако поскольку аргумент display() имеет тип Bear, то объявленная в ZooAnimal функция-друг добавляется в множество кандидатов.
Таким образом, если при вызове обычной функции задан аргумент, который представляет собой объект класса, ссылку или указатель на объект класса, то множество функций-кандидатов является объединением следующих множеств:
Наследование влияет также на построение множества кандидатов для вызова функции-члена с помощью операторов “точка” или “стрелка”. В разделе 18.4 мы говорили, что объявление функции-члена в производном классе не перегружает, а скрывает одноименные функции-члены в базовом, даже если их списки параметров различны:
class ZooAnimal {
public:
Time feeding_time( string );
// ...
};
class Bear : public ZooAnimal {
public:
// скрывает ZooAnimal::feeding_time( string )
Time feeding_time( int );
// ...
};
Bear Winnie;
// ошибка: ZooAnimal::feeding_time( string ) скрыта
Winnie.feeding_time( "Winnie" );
Функция-член feeding_time(int), объявленная в классе Bear, скрывает feeding_time(string), объявленную в ZooAnimal, базовом для Bear. Поскольку функция-член вызывается через объект Winnie типа Bear, то при поиске кандидатов для этого вызова просматривается только область видимости класса Bear, и единственным кандидатом будет feeding_time(int). Так как других кандидатов нет, вызов считается ошибочным.