Как определяется приоритет неявного преобразования типов?
Вот код:
class A{
public:
int val;
char cval;
A():val(10),cval('a'){ }
operator char() const{ return cval; }
operator int() const{ return val; }
};
int main()
{
A a;
cout << a;
}
Я бегу код в VS 2013, выходное значение 10
если я закомментирую operator int() const{ return val; }
, тогда выходное значение станет a
,
Мой вопрос заключается в том, как компилятор определяет, какое неявное преобразование типов выбрать, я имею в виду, поскольку оба int
а также char
Возможны варианты для <<
оператор?
3 ответа
Да, это неоднозначно, но причина неоднозначности на самом деле довольно удивительна. Дело не в том, что компилятор не может различить ostream::operator<<(int)
а также operator<<(ostream &, char)
; последний на самом деле является шаблоном, а первый - нет, поэтому, если совпадения одинаково хороши, будет выбран первый, и между этими двумя нет никакой двусмысленности. Скорее, двусмысленность исходит от ostream
другой участник operator<<
Перегрузки.
Минимизированное воспроизведение
struct A{
operator char() const{ return 'a'; }
operator int() const{ return 10; }
};
struct B {
void operator<< (int) { }
void operator<< (long) { }
};
int main()
{
A a;
B b;
b << a;
}
Проблема в том, что преобразование a
в long
может быть через a.operator char()
или же a.operator int()
, оба сопровождаются стандартной последовательностью преобразования, состоящей из интегрального преобразования. Стандарт гласит, что (§13.3.3.1 [over.best.ics]/p10, сноска опущена):
Если существует несколько различных последовательностей преобразований, каждая из которых преобразует аргумент в тип параметра, последовательность неявного преобразования, связанная с параметром, определяется как уникальная последовательность преобразования, обозначенная неоднозначной последовательностью преобразования. В целях ранжирования последовательностей неявного преобразования, как описано в 13.3.3.2, неоднозначная последовательность преобразования обрабатывается как определенная пользователем последовательность, которая неотличима от любой другой определенной пользователем последовательности преобразования. *
Поскольку преобразование a
в int
также включает в себя пользовательскую последовательность преобразования, она неотличима от неоднозначной последовательности преобразования из a
в long
и в этом контексте никакое другое правило в §13.3.3 [over.match.best] также не применяется для различения двух перегрузок. Следовательно, вызов неоднозначен, а программа плохо сформирована.
* Следующее предложение в стандарте гласит: "Если функция, которая использует неоднозначную последовательность преобразования, выбрана в качестве наилучшей жизнеспособной функции, вызов будет некорректным, поскольку преобразование одного из аргументов в вызове является неоднозначным". не кажется правильным, но подробное обсуждение этого вопроса, вероятно, лучше в отдельном вопросе.
Это не должно компилироваться, так как преобразование неоднозначно; и это не с моим компилятором: живая демонстрация. Я понятия не имею, почему ваш компилятор принимает это, или как он выбирает, какое преобразование использовать, но это неправильно.
Вы можете устранить неоднозначность с явным приведением:
cout << static_cast<char>(a); // uses operator char()
cout << static_cast<int>(a); // uses operator int()
Лично я бы, вероятно, использовал именованные функции преобразования, а не операторы, если бы я хотел, чтобы он был конвертируемым в несколько типов.
Сеанс отладки дал результат. Один определяется глобально operator<<
а другой - метод класса. Вы догадываетесь, кто из них звонит.
Test.exe!std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > & _Ostr, char _Ch)
msvcp120d.dll!std::basic_ostream<char,std::char_traits<char> >::operator<<(int _Val) Line 292 C++
Я не адвокат по языку, но я считаю, что компилятор в первую очередь отдает приоритет функции-члену.