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

вот код:

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С другой перегрузок.

A минимизированный repro is

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 to long может быть через любой a.operator char() или a.operator int(), за обоими следует стандартная последовательность преобразования, состоящая из интегрального преобразования. В стандарте говорится, что (§13.3.3.1 [окончено.лучший.ics] / p10, сноска опущена):

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

С момента преобразования a to int также включает определяемую пользователем последовательность преобразования, она неотличимый от неоднозначная последовательность преобразования С a to long, и в этом контексте нет другого правила в §13.3.3 [over.спичка.best] применяется для различения двух перегрузок. Следовательно, вызов неоднозначен, а программа плохо сформирована.


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


он не должен компилироваться, так как преобразование неоднозначно; и это не с моим компилятором: live demo. Я понятия не имею, почему ваш компилятор принимает его или как он выбирает, какое преобразование использовать, но это неправильно.

вы можете разрешить двусмысленность с явным приведением:

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

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