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

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

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

class SmallInt {

public:

SmallInt( int );

};

extern void calc( double );

extern void calc( SmallInt );

int ival;

int main() {

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

}

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

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

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

class Number {

public:

operator SmallInt();

operator int();

// ...

};

extern void calc( int );

extern void calc( SmallInt );

extern Number num;

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

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

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

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

calc( static_cast int ( num ) );

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

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

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

calc( num ); // по-прежнему неоднозначен?

по-прежнему неоднозначен? Вспомните, что в SmallInt также есть конвертер, способный преобразовать значение типа SmallInt в int.

class SmallInt {

public:

operator int();

// ...

};

Можно предположить, что функция calc() вызывается, если сначала преобразовать фактический аргумент num из типа Number в тип SmallInt с помощью конвертера Number::operator SmallInt(), а затем результат привести к типу int с помощью SmallInt::operator SmallInt(). Однако это не так. Напомним, что в последовательность определенных пользователем преобразований может входит несколько стандартных преобразований, но лишь одно пользовательское. Если конвертер Number::operator int() не определен, то функция calc(int) не считается устоявшей, поскольку не существует неявного преобразования из типа фактического аргумента num в тип формального параметра int.

Поэтому в отсутствие конвертера Number::operator int() единственной устоявшей функцией будет calc(SmallInt), в пользу которой и разрешается вызов.

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

class SmallInt {

public:

operator int();

// ...

};

void manip( int );

void manip( char );

SmallInt si ( 68 );

main() {

manip( si ); // вызывается manip( int )

}

Как manip(int), так и manip(char) являются устоявшими функциями; первая – потому, что конвертер SmallInt::operator int() преобразует фактический аргумент типа SmallInt в тип формального параметра int, а вторая – потому, что тот же конвертер преобразует SmallInt в int, после чего результат с помощью стандартного преобразования приводится к типу char. Последовательности определенных пользователем преобразований выглядят так:

manip(int) : operator int()-точное соответствие

manip(int) : operator int()-стандартное преобразование

Поскольку в обеих последовательностях используется один и тот же конвертер, то для определения лучшей из них анализируется ранг последовательности стандартных преобразований. Так как точное соответствие лучше преобразования, то наилучшей из устоявших будет функция manip(int).

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

class SmallInt {

public:

operator int();

operator float();

// ...

};

void compute( float );

void compute( char );

SmallInt si ( 68 );

main() {

compute( si ); // неоднозначность

}

И compute(float), и compute(int) – устоявшие функции. compute(float) – потому, что конвертер SmallInt::operator float()преобразует аргумент типа SmallInt в тип параметра float, а compute(char) – потому, что SmallInt::operator int() преобразует аргумент типа SmallInt в тип int, после чего результат стандартно приводится к типу char. Таким образом, имеются последовательности:

compute(float) : operator float()-точное соответствие

compute(char) : operator char()-стандартное преобразование

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

Упражнение 15.12

В классах стандартной библиотеки C++ нет определений конвертеров, а большинство конструкторов, принимающих один параметр, объявлены явными. Однако определено множество перегруженных операторов. Как вы думаете, почему при проектировании было принято такое решение?

Упражнение 15.13

Почему перегруженный оператор ввода для класса SmallInt, определенный в начале этого раздела, реализован не так:

istream& operator&&( istream &is, SmallInt &si )

{

return ( is is.value );

}

Упражнение 15.14

Приведите возможные последовательности определенных пользователем преобразований для следующих инициализаций. Каким будет результат каждой инициализации?

class LongDouble {

operator double();

operator float();

};

extern LongDouble ldObj;

(a) int ex1 = ldObj;

(b) float ex2 = ldObj;

Упражнение 15.15

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

Упражнение 15.16

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

class LongDouble {

public:

LongDouble( double );

// ...

};

extern void calc( int );

extern void calc( LongDouble );

double dval;

int main() {

calc( dval ); // какая функция?

}

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг

Ранжирование

Из книги ИНФОРМАЦИОННАЯ ТЕХНОЛОГИЯ ОЦЕНКА ПРОГРАММНОЙ ПРОДУКЦИИ ХАРАКТЕРИСТИКИ КАЧЕСТВА И РУКОВОДСТВА ПО ИХ ПРИМЕНЕНИЮ автора Автор неизвестен


5.3.3.2 Ранжирование

Из книги Программирование на языке Ruby [Идеология языка, теория и практика применения] автора Фултон Хэл

5.3.3.2 Ранжирование На этапе ранжирования устанавливается уровень ранжирования для измеренного значения (см. схему


11.3.8. Получение списка определенных сущностей

Из книги Разработка приложений в среде Linux. Второе издание автора Джонсон Майкл К.

11.3.8. Получение списка определенных сущностей API отражения в Ruby позволяет опрашивать классы и объекты во время выполнения. Рассмотрим методы, имеющиеся для этой цели в Module, Class и Object.В модуле Module есть метод constants, который возвращает массив всех констант, определенных в


21.3.3. Тестирование последовательностей

Из книги C++. Сборник рецептов автора Диггинс Кристофер

21.3.3. Тестирование последовательностей Для проверки большинства последовательностей вам нужно просто войти в виртуальную консоль и запустить cat. Введите последовательности, которые вы хотите протестировать, и увидите результаты. Для ^[ нажмите клавишу <Esc>.Терминалы


7.8. Выполнение для последовательностей операций над множествами

Из книги Яндекс для всех автора Абрамзон М. Г.

7.8. Выполнение для последовательностей операций над множествами ПроблемаИмеются последовательности, которые требуется реорганизовать с помощью операций над множествами, таких как объединение (union), различие (difference) или пересечение (intersection).РешениеДля этой цели


9.4. Факторы, влияющие на ранжирование

Из книги Искусство программирования на языке сценариев командной оболочки автора Купер Мендель

9.4. Факторы, влияющие на ранжирование В ряде опубликованных в Интернете статей: "118 факторов ранжирования в Google"(http://webest.info/seo/google/google-pr-ratings.php), "Факторы, влияющие на ранкинг в поисковой системе" (http://www.master-x.com/articles/ printmode.html?id=270) была сделана попытка собрать и классифицировать


Пример 25-10. Исследование математических последовательностей

Из книги Интернет-маркетинг. Полный сборник практических инструментов автора Вирин Федор Юрьевич

Пример 25-10. Исследование математических последовательностей #!/bin/bash# Пресловутая "Q-последовательность" Дугласа Хольфштадтера *Douglas Hofstadter):# Q(1) = Q(2) = 1# Q(n) = Q(n - Q(n-1)) + Q(n - Q(n-2)), для n>2# Это "хаотическая" последовательность целых чисел с непредсказуемым поведением.# Первые 20


Факторы, влияющие на ранжирование результатов поиска в поисковых машинах

Из книги Linux и UNIX: программирование в shell. Руководство разработчика. автора Тейнсли Дэвид

Факторы, влияющие на ранжирование результатов поиска в поисковых машинах Поисковая машина выстраивает сайты в результатах поиска в соответствии с их релевантностью введенному запросу, то есть по соответствию сайта некоему «эталону», который она должна показать


Влияние перелинковки на ранжирование

Из книги автора

Влияние перелинковки на ранжирование Для продвижения высоко– и среднеконкурентных запросов внутренней перелинковки однозначно не хватит, но определенную роль она все же играет. Что же касается низкоконкурентных запросов, то тут внутренние ссылки способны стать


Применение заранее определенных моделей

Из книги автора

Применение заранее определенных моделей В Qt заранее определено несколько моделей, предназначенных для использования с классами представлений:• QStringListModel — хранит список строк;• QStandardltemModel — хранит данные произвольной иерархической структуры;• QDirModel — формирует


Методы последовательностей

Из книги автора

Методы последовательностей Все последовательности имеют множество методов обработки последовательностей, реализованных как методы расширения.Список методов последовательностей* Методы Print* Метод фильтрации Where* Метод проецирования Select* Метод проецирования SelectMany*


Методы для последовательностей

Из книги автора

Методы для последовательностей Методы Print Описание методовМетоды приведены для последовательности sequence of T. function Print(delim: string := ): sequence of T; Выводит последовательность на экран, используя delim в качестве разделителя. function Println(delim: string := ): sequence of T; Выводит