struct В {

 operator A() const; // тоже преобразует В в A

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

};

A f (const A&);

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

            // или f(A::A(const B&))

Поскольку существуют два способа получения объекта класса А из В, компилятор не знает, какой из них использовать; поэтому вызов функции f() неоднозначен. Для получения объекта класса В этот вызов может использовать конструктор класса А или оператор преобразования класса В, преобразующий объект класса В в А. Поскольку обе эти функции одинаково хороши, вызов неоднозначен и ошибочен.

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

A a1 = f(b.operator А()); // ok: использовать оператор преобразования В

A а2 = f(A(b));           // ok: использовать конструктор класса А

Обратите внимание: нельзя решить неоднозначность при помощи приведения — у самого приведения будет та же двусмысленность.

Двусмысленность и множественность путей преобразования во встроенные типы

Двусмысленность возникает также в случае, когда класс определяет несколько преобразований в (или из) типы, которые сами связываются преобразованиями. Самый простой и наглядный пример (а также особенно проблематичный) — это когда класс определяет конструкторы преобразования в или из более, чем один арифметический тип.

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

struct A {

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

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

 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.

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

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

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

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

Перейти на страницу:

Похожие книги