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

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


class SmallInt { public:

operator int(); ...

последовательности стандартных преобразований, выполняемых после его вызова:

void manip( int ); void manip( char );

SmallInt si ( 68 );

main() {

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

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

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

пользователем преобразований выглядят так:

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

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

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

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

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

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



class SmallInt { public:

operator int(); operator float();

за применением конвертера. Например:

void compute( float ); void compute( char );

char

SmallInt si ( 68 );

main() {

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

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

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

приводится к типу char. Таким образом, имеются последовательности:

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

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

Упражнение 15.12

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

Упражнение 15.13

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

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



class LongDouble { operator double(); operator float();

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

(a) int ex1 = ldObj; extern LongDouble 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 ); какая функция?

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

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

return ( is >> is.value );

Упражнение 15.14

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



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

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