Язык программирования C++. Вводный курс

Функции-кандидаты


Функцией-кандидатом называется функция, имеющая то же имя, что и вызванная. Кандидаты отыскиваются двумя способами:

·

объявление функции видимо в точке вызова. В следующем примере

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*):


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

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;

<


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

func( args );

или функции-члена с помощью операторов доступа “точка” или “стрелка”:

object.memfunc( args );

pointer->memfunc( args );

В данном разделе мы изучим оба случая.

Если аргумент обычной функции имеет тип класса, ссылки или указателя на тип класса, и класс определен в пространстве имен, то кандидатами будут все одноименные функции, объявленные в этом пространстве, даже если они невидимы в точке вызова (подробнее об этом говорилось в разделе 15.10). Если аргумент при наследовании имеет тип класса, ссылки или указателя на тип класса, и у этого класса есть базовые, то в множество кандидатов добавляются также функции, объявленные в тех пространствах имен, где определены базовые классы. Например:

namespace NS {

   class ZooAnimal { /* ... */ };

   void display( const ZooAnimal& );

}

// базовый класс Bear объявлен в пространстве имен NS

class Bear : public NS::ZooAnimal { };

int main() {

   Bear baloo;

   display( baloo );

   return 0;

}

Аргумент baloo

имеет тип класса Bear. Кандидатами для вызова display() будут не только функции, объявления которых видимы в точке ее вызова, но также и те, что объявлены в пространствах имен, в которых объявлены класс Bear и его базовый класс ZooAnimal. Поэтому в множество кандидатов добавляется функция display(const ZooAnimal&), объявленная в пространстве имен NS.

Если аргумент имеет тип класса и в определении этого класса объявлены функции-друзья с тем же именем, что и вызванная функция, то эти друзья также будут кандидатами, даже если их объявления не видны в точке вызова (см. раздел 15.10). Если аргумент при наследовании имеет тип класса, у которого есть базовые, то в множество кандидатов добавляются одноименные функции-друзья каждого из них. Предположим, что в предыдущем примере display() объявлена как функция-друг ZooAnimal:




Рассмотрим два вида вызовов функции-члена:

mc.mf( arg );

pmc->mf( arg );

где mc – выражение типа myClass, а pmc – выражение типа “указатель на тип myClass”. Множество кандидатов для обоих вызовов составлено из функций, найденных в области видимости класса myClass при поиске объявления mf().

Аналогично для вызова функции вида

myClass::mf( arg );

множество кандидатов также состоит из функций, найденных в области видимости класса myClass при поиске объявления mf(). Например:

class myClass {

public:

   void mf( double );

   void mf( char, char = '\n' );

   static void mf( int* );

   // ...

};

int main() {

   myClass mc;

   int iobj;

   mc.mf( iobj );

}

Кандидатами для вызова функции в main()

являются все три функции-члена mf(), объявленные в myClass:

void mf( double );

void mf( char, char = '\n' );

static void mf( int* );

Если бы в myClass не было объявлено ни одной функции-члена с именем mf(), то множество кандидатов оказалось бы пустым. (На самом деле рассматривались бы также и функции из базовых классов. О том, как они попадают в это множество, мы поговорим в разделе 19.3.) Если для вызова функции не оказывается кандидатов, компилятор выдает сообщение об ошибке.



Содержание раздела