Категории
Самые читаемые
Лучшие книги » Компьютеры и Интернет » Программирование » Язык программирования C++. Пятое издание - Стенли Липпман

Язык программирования C++. Пятое издание - Стенли Липпман

Читать онлайн Язык программирования C++. Пятое издание - Стенли Липпман

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 194 195 196 197 198 199 200 201 202 ... 297
Перейти на страницу:

 operator int() const;    // обычно плохая идея иметь два

 operator double() const; // преобразования в арифметические типы

 // другие члены

};

void f2(long double);

A a;

f2(a); // ошибка неоднозначности: f(A::operator int())

       // или f (A::operator double ())

long lg;

A a2(lg); // ошибка неоднозначности: A::A(int) или A::A(double)

В вызове функции f2() ни одно из преобразований не соответствует точно типу long double. Но для его получения применимо любое преобразование, сопровождаемое стандартным преобразованием. Следовательно, никакое из преобразований не лучше другого, значит, вызов неоднозначен.

Возникает та же проблема, что и при попытке инициализации объекта a2 значением типа long. Ни один из конструкторов не соответствует точно типу long. Каждый требовал преобразования аргумента прежде, чем использовать конструктор.

• Стандартное преобразование long в double, затем A(double).

• Стандартное преобразование long в int, затем A(int).

Эти последовательности преобразований равнозначны, поэтому вызов неоднозначен.

Вызов функции f2() и инициализация объекта a2 неоднозначны, поскольку у необходимых стандартных преобразований одинаковый ранг (см. раздел 6.6.1). Когда используется пользовательское преобразование, ранг стандартного преобразования, если таковые вообще имеются, позволяет выбрать наилучшее соответствие:

short s = 42;

// преобразование short в int лучше, чем short в double

А a3(s); // используется A::A(int)

В данном случае преобразование short в int предпочтительней, чем short в double. Следовательно, объект a3 создается с использованием конструктора A::A(int), который запускается для преобразования значения s.

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

Перегруженные функции и конструкторы преобразования

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

Например, могут возникнуть проблемы неоднозначности, когда перегруженные функции получают параметры, отличающиеся типами классов, которые определяют те же конструкторы преобразования:

Внимание! Преобразования и операторы

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

• Никогда не создавайте взаимных преобразований типов. Другими словами, если класс Foo имеет конструктор, получающий объект класса Bar, не создавайте в классе Bar оператор преобразования для типа Foo.

• Избегайте преобразований во встроенные арифметические типы. Но если преобразование в арифметический тип необходимо, то придется учесть следующее.

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

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

Самое простое правило: за исключением явного преобразования в тип bool, избегайте создания функций преобразования и ограничьте неявные конструкторы теми, которые безусловно необходимы.

struct C {

 C(int);

 // другие члены

};

struct D {

 D(int);

 // другие члены

};

void manip(const С&);

void manip(const D&);

manip(10); // ошибка неоднозначности: manip(С(10)) или manip(D(10))

Здесь у структур С и D есть конструкторы, получающие значение типа int. Для версий функции manip() подходит любой конструктор. Следовательно, вызов неоднозначен: он может означать преобразование int в С и вызов первой версии manip() или может означать преобразование int в D и вызов второй версии.

Вызывающая сторона может устранить неоднозначность при явном создании правильного типа:

manip(С(10)); // ok: вызов manip(const C&)

Необходимость в использовании конструктора или приведения для преобразования аргумента при обращении к перегруженной функции — это признак плохого проекта.

Перегруженные функции и пользовательские преобразования

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

Например, вызов функции manip() был бы неоднозначен, даже если бы один из классов определил конструктор, который требовал бы для аргумента стандартного преобразования:

struct E {

 E(double);

 // другие члены

};

void manip2(const C&);

void manip2(const E&);

// ошибка неоднозначности: применимы два разных пользовательских

// преобразования

manip2(10); // manip2(C(10) или manip2(E(double(10)))

В данном случае у класса С есть преобразование из типа int и у класса E есть преобразование из типа double. Для вызова manip2(10) подходят обе версии функции manip2():

• Версия manip2(const C&) подходит потому, что у класса С есть конструктор преобразования, получающий тип int. Этот конструктор точно соответствует аргументу.

• Версия manip2(const E&) подходит потому, что у класса E есть конструктор преобразования, получающий тип double и возможность использовать стандартное преобразование для преобразования аргумента типа int, чтобы использовать этот конструктор преобразования.

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

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

Упражнения раздела 14.9.2

Упражнение 14.50. Представьте возможные последовательности преобразований типов для инициализации объектов ex1 и ex2. Объясните, допустима ли их инициализация или нет.

struct LongDouble {

 LongDouble(double = 0.0);

 operator double();

 operator float();

};

LongDouble IdObj;

int ex1 = IdObj;

float ex2 = IdObj;

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

void calc(int);

void calc(LongDouble);

double dval;

calc(dval); // которая calc()?

14.9.3. Подбор функций и перегруженные операторы

Перегруженные операторы — это перегруженные функции. При выявлении, который из встроенных или перегруженных операторов применяется для данного выражения, используется обычный подбор функции (см. раздел 6.4). Однако, когда в выражении используется функция оператора, набор функций-кандидатов шире, чем при вызове функций, использующих оператор вызова. Если объект а имеет тип класса, то выражение a sym b может быть следующим:

1 ... 194 195 196 197 198 199 200 201 202 ... 297
Перейти на страницу:
На этой странице вы можете бесплатно скачать Язык программирования C++. Пятое издание - Стенли Липпман торрент бесплатно.
Комментарии