Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Детали разрешения перегрузки функций



В разделе 9.2 мы уже упоминали, что процесс разрешения перегрузки функций состоит из трех шагов:

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

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

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

Теперь мы готовы к тому, чтобы изучить эти шаги более детально.

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

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

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

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, дополнительное пространство имен в поисках других кандидатов не рассматривается.

Устоявшие функции

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

В следующем примере для вызова f(5.6) есть две устоявшие функции: f(int) и f(double).

void f();

void f( int );

void f( double );

void f( char*, char* );

 

int main() {

f( 5.6 ); // 2 устоявшие функции: f( int ) и f( double )

return 0;

}

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

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

char* format( int );

void g() {

// глобальная функция format( int ) скрыта

char* format( double );

char* format( char* );

format(3); // есть только одна устоявшая функция: format( double )

}

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

namespace libs_R_us {

int max( int, int );

double max( double, double );

}

 

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

using libs_R_us:: max;

 

char max( char, char );

void func()

{

// все три функции max() являются устоявшими

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

}

Обратите внимание, что функция-кандидат с несколькими параметрами исключается из числа устоявших, как только выясняется, что один из фактических аргументов не может быть приведен к типу соответствующего формального параметра, пусть даже для всех остальных аргументов такое преобразование существует. В следующем примере функция min(char *, int) исключается из множества устоявших, поскольку нет возможности трансформации типа первого аргумента int в тип соответствующего параметра char *. И это происходит несмотря на то, что второй аргумент точно соответствует второму параметру.

extern double min( double, double );

extern double min( char*, int );

 

void func()

{

// одна функция-кандидат min( double, double )

min( 87, 65 ); // вызывается min( double, double )

}

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

void print( unsigned int );

void print( char* );

void print( char );

 

int *ip;

class SmallInt { /*... */ };

SmallInt si;

 

int main() {

print( ip ); // ошибка: нет устоявших функций: соответствие не найдено

print( si ); // ошибка: нет устоявших функций: соответствие не найдено

return 0;

}


Поделиться:



Последнее изменение этой страницы: 2019-04-09; Просмотров: 270; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.043 с.)
Главная | Случайная страница | Обратная связь