Как определяется приоритет неявного преобразования типов?

Вот код:

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++

Я не адвокат по языку, но я считаю, что компилятор в первую очередь отдает приоритет функции-члену.

Другие вопросы по тегам