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

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

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

15.10.3. Функции-кандидаты для вызова функции в области видимости класса

Когда вызов функции вида

calc(t)

встречается в области видимости класса (например, внутри функции-члена), то первая часть множества кандидатов, описанного в предыдущем подразделе (т.е. множество, включающее объявления функций, видимых в точке вызова), может содержать не только функции-члены класса. Для построения такого множества применяется разрешение имени. (Эта тема детально разбиралась в разделах 13.9 – 13.12.)

Рассмотрим пример:

namespace NS {

struct myClass {

void k( int );

static void k( char* );

void mf();

};

int k( double );

};

void h(char);

void NS::myClass::mf() {

h('a'); // вызывается глобальная h( char )

k(4); // вызывается myClass::k( int )

}

Как отмечалось в разделе 13.11, квалификаторы NS::myClass:: просматриваются в обратном порядке: сначала поиск видимого объявления для имени, использованного в определении функции-члена mf(), ведется в классе myClass, а затем – в пространстве имен NS. Рассмотрим первый вызов:

h( 'a' );

При разрешении имени h() в определении функции-члена mf() сначала просматриваются функции-члены myClass. Поскольку функции-члена с таким именем в области видимости этого класса нет, то далее поиск идет в пространстве имен NS. Функции h()нет и там, поэтому мы переходим в глобальную область видимости. Результат – глобальная функция h(char), единственная функция-кандидат, видимая в точке вызова.

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