C++ для начинающих.

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() {

// в точке вызова не видна ни одна из функций takeC().

TakeC( cobj); // правильно: вызывается NS::takeC( C ),

// потому что аргумент имеет тип NS::C, следовательно,

// принимается во внимание функция takeC(),

// объявленная в пространстве имен NS.

Return 0;

}

*

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

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

Функция, объявленная во вложенной области видимости, скрывает, а не перегружает одноименную функцию во внешней области. В такой ситуации кандидатами будут только функции из во вложенной области, т.е. такие, которые не скрыты при вызове. В следующем примере функциями-кандидатами, видимыми в точке вызова, являются format(double) и format(char*):

Char* format( int );

Void g() {

Char *format( double );

Char* format( char* );

Format(3); // вызывается format( double ).

}

Так как format(int), объявленная в глобальной области видимости, скрыта, она не включается в множество функций-кандидатов.

Кандидаты могут быть введены с помощью using-объявлений, видимых в точке вызова:

Namespace libs_R_us {

Int max( int, int );

Double max( double, double );

}

Char max( char, char );

Void func().

{

// функции из пространства имен невидимы.

// все три вызова разрешаются в пользу глобальной функции max( char, char ).

Max( 87, 65 );

Max( 35.5, 76.6 );

Max( 'J', 'L' );

}

Функции max(), определенные в пространстве имен libs_R_us, невидимы в точке вызова. Единственной видимой является функция max() из глобальной области; только она входит в множество функций-кандидатов и вызывается при каждом из трех обращений к func(). Мы можем воспользоваться using-объявлением, чтобы сделать видимыми функции max() из пространства имен libs_R_us. Куда поместить using-объявление? Если включить его в глобальную область видимости:

Char max( char, char );

Using libs_R_us::max; // using-объявление.

То функции max() из libs_R_us добавляются в множество перегруженных функций, которое уже содержит max(), объявленную в глобальной области. Теперь все три функции видны внутри func() и становятся кандидатами. В этой ситуации вызовы func() разрешаются следующим образом:

Void func().

{

Max( 87, 65 ); // вызывается libs_R_us::max( int, int ).

Max( 35.5, 76.6 ); // вызывается libs_R_us::max( double, double ).

Max( 'J', 'L' ); // вызывается ::max( char, char ).

}

Но что будет, если мы введем using-объявление в локальную область видимости функции func(), как показано в данном примере?

Void func().

{

// using-объявление.

Using libs_R_us::max;

// те же вызовы функций, что и выше.

}

Какие из функций max() будут включены в множество кандидатов? Напомним, что using-объявления вкладываются друг в друга. При наличии такого объявления в локальной области глобальная функция max(char, char) оказывается скрытой, так что в точке вызова видны только.

Libs_R_us::max( int, int );

Libs_R_us::max( double, double );

Они и являются кандидатами. Теперь вызовы func() разрешаются следующим образом:

Void func().

{

// using-объявление.

// глобальная функция max( char, char ) скрыта.

Using libs_R_us::max;

Max( 87, 65 ); // вызывается libs_R_us::max( int, int ).

Max( 35.5, 76.6 ); // вызывается libs_R_us::max( double, double ).

Max( 'J', 'L' ); // вызывается libs_R_us::max( int, int ).

}

Using-директивы также оказывают влияние на состав множества функций-кандидатов. Предположим, мы решили их использовать, чтобы сделать функции max() из пространства имен libs_R_us видимыми в func(). Если разместить следующую using-директиву в глобальной области видимости, то множество функций-кандидатов будет состоять из глобальной функции max(char, char) и функций max(int, int) и max(double, double), объявленных в libs_R_us:

Namespace libs_R_us {

Int max( int, int );

Double max( double, double );

}

Char max( char, char );

Using namespace libs_R_us; // using-директива.

Void func().

{

Max( 87, 65 ); // вызывается libs_R_us::max( int, int ).

Max( 35.5, 76.6 ); // вызывается libs_R_us::max( double, double ).

Max( 'J', 'L' ); // вызывается ::max( int, int ).

}

Что будет, если поместить using-директиву в локальную область видимости, как в следующем примере?

Void func().

{

// using-директива.

Using namespace libs_R_us;

// те же вызовы функций, что и выше.

}

Какие из функций max() окажутся среди кандидатов? Напомним, что using-директива делает члены пространства имен видимыми, словно они были объявлены вне этого пространства, в той точке, где такая директива помещается. В нашем примере члены libs_R_us видимы в локальной области функции func(), как будто они объявлены вне пространства – в глобальной области. Отсюда следует, что множество перегруженных функций, видимых внутри func(), то же, что и раньше, т.е. включает в себя.

Max( char, char );

Libs_R_us::max( int, int );

Libs_R_us::max( double, double );

В локальной или глобальной области видимости появляется using-директива, на разрешение вызовов функции func() не влияет:

Void func().

{

Using namespace libs_R_us;

Max( 87, 65 ); // вызывается libs_R_us::max( int, int ).

Max( 35.5, 76.6 ); // вызывается libs_R_us::max( double, double ).

Max( 'J', 'L' ); // вызывается ::max( int, int ).

}

Итак, множество кандидатов состоит из функций, видимых в точке вызова, включая и те, которые введены using-объявлениями и using-директивами, а также из функций, объявленных в пространствах имен, ассоциированных с типами фактических аргументов. Например:

Namespace basicLib {

Int print( int );

Double print( double );

}

Namespace matrixLib {

Class matrix { /* ... */ };

Void print( const maxtrix );

}

Void display().

{

Using basicLib::print;

MatrixLib::matrix mObj;

Print( mObj ); // вызывается maxtrixLib::print( const maxtrix ).

Print( 87 ); // вызывается basicLib::print( const maxtrix ).

}

Кандидатами для print(mObj) являются введенные using-объявлением внутри display() функции basicLib::print(int) и basicLib::print(double), поскольку они видимы в точке вызова. Так как фактический аргумент функции имеет тип matrixLib::matrix, то функция print(), объявленная в пространстве имен matrixLib, также будет кандидатом. Каковы функции-кандидаты для print(87)? Только basicLib::print(int) и basicLib::print(double), видимые в точке вызова. Поскольку аргумент имеет тип int, дополнительное пространство имен в поисках других кандидатов не рассматривается.