Программирование >>  Инициализация объектов класса, структура 

1 ... 249 250 251 [ 252 ] 253 254 255 ... 395


namespace NS { struct myClass void k( int

static void

void mf();

k( char* );

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) , единственная функция-кандидат, видимая в точке вызова.

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

k( 4 );

Сначала поиск ведется в области видимости класса myClass. При этом найдены две функции-члена k(int) и k(char*) . Поскольку множество кандидатов содержит лишь функции, объявленные в той области, где разрешение успешно завершилось, то пространство имен NS не просматривается и функция k(double) в данное множество не включается.

calc(t)

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



class SmallInt { public:

SmallInt( int );

extern void calc( double ); extern void calc( SmallInt ); int ival;

int main() {

calc ( ival ); какая calc() вызается?

calc() , то какая функция будет вызвана?

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

Последовательность стандартных преобразований всегда лучше последовательности определенных пользователем преобразований. Так, при вызове calc() из примера выше обе функции calc() являются устоявшими. calc(double) устояла потому, что существует стандартное преобразование типа фактического аргумента int в тип формального параметра double, а calc(SmallInt) - потому, что имеется определенное пользователем преобразование из int в SmallInt, которое использует конструктор SmiallInt(int). Следовательно, наилучшей из устоявших функций будет calc(double).

А как сравниваются две последовательности определенных пользователем преобразований? Если в них используются разные конвертеры или разные конструкторы, то обе такие последовательности считаются одинаково хорошими:

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

15.10.4. Ранжирование последовательностей определенных пользователем преобразований

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



class Number { public:

operator SmallInt(); operator int();

...

extern void calc( int ); extern void calc( SmallInt ); extern Number num;

calc( num ); ошибка: неоднозначность

Устоявшими окажутся и calc(int), и calc(SmiallInt); первая - поскольку конвертер Number::operator int() преобразует фактический аргумент типа Number в формальный параметр типа int, а вторая потому, что конвертер Number::operator SmallInt() преобразует фактический аргумент типа Number в формальный параметр типа SmallInt. Так как последовательности определенных пользователем преобразований всегда имеют одинаковый ранг, то компилятор не может выбрать, какая из них лучше. Таким образом, этот вызов функции неоднозначен и приводит к ошибке компиляции.

явное указание преобразования устраняет неоднозначность

Есть способ разрешить неоднозначность, указав преобразование явно:

calc( static cast< int >( num ) );

Явное приведение типов заставляет компилятор преобразовать аргумент num в тип int с помощью конвертера Number::operator int() . Фактический аргумент тогда будет иметь тип int, что точно соответствует функции calc(int) , которая и выбирается в качестве наилучшей.

Допустим, в классе Numiber не определен конвертер Numiber: :operator int(). Будет ли

определен только Numer::operator SmallInt()

тогда вызов

calc( num ); по-прежнему неоднозначен? но-нрежнему неоднозначен? Вспомните, что в SmiallInt также есть конвертер, способный

class SmallInt {

public:

operator int();

...

преобразовать значение типа SmallInt в int.

Можно предположить, что функция calc() вызывается, если сначала преобразовать фактический аргумент num из типа Number в тип SmallInt с помощью конвертера



1 ... 249 250 251 [ 252 ] 253 254 255 ... 395

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика