Есть способ разрешить неоднозначность, указав преобразование явно:
// явное указание преобразования устраняет неоднозначность
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()-точное соответствие