Подтвердить что ты не робот

Разрешение перегрузки с несколькими функциями и несколькими операторами преобразования

Рассмотрим простой код:

#include<iostream>

struct A {
    operator double(){
        std::cout<<"Conversion function double chosen."<<std::endl;
        return 1.1;
    }
    operator char(){
        std::cout<<"Conversion function char chosen."<<std::endl;
        return 'a';
    }
} a;

void foo(int){}
void foo (char){}
int main() {
    foo(a);
}

Над кодом работает отлично, и как ожидалось gcc, clang и VС++ выбирает foo(char).

Теперь немного измените код:

#include<iostream>

struct A {
    operator double(){
        std::cout<<"Conversion function double chosen."<<std::endl;
        return 1.1;
    }
    operator char(){
        std::cout<<"Conversion function char chosen."<<std::endl;
        return 'a';
    }
} a;

void foo(int){}
void foo (double){} //parameter changed from char to double
int main() {
    foo(a);
}

Теперь это должно выбрать foo(double), но кажется, что только VС++ доволен кодом, а clang и gcc недовольны приведенным выше кодом.

main.cpp:11:10: error: call of overloaded 'foo(A&)' is ambiguous
 foo(a);
     ^
main.cpp:8:6: note: candidate: void foo(int)
 void foo(int){}
      ^
main.cpp:9:6: note: candidate: void foo(double)
 void foo (double){} //parameter changed from char to double
      ^

Может кто-нибудь объяснить, почему код выше не работает? или это ошибка?.

Еще один вопрос: У меня есть код gcc и clang для разрешения перегрузки?

4b9b3361

Ответ 1

TL; DR. Разница заключается в том, что в первом случае, в отличие от второго, пользовательские последовательности преобразования (A -> char, A -> int) вызывают одну и ту же функцию преобразования ( operator char). Это позволяет нам разорвать связь через [over.ics.rank]/(3.3).


Лучшие операторы преобразования для определенных функций выбираются [over.match.best]/(1.4) (сравнение последовательностей преобразования их возвращаемые типы).

Следовательно, лучшей функцией преобразования для foo(int) является operator char, за которой следует продвижение до int, в отличие от operator double, за которым следует преобразование с плавающей точкой.

Теперь рассмотрим оба варианта второй перегрузки:

  • Лучший ICS для foo(char) - также через operator char (идентичность лучше, чем преобразование с плавающей запятой). Таким образом [over.ics.rank]/(3.3) применимо:

    Пользовательская последовательность преобразования U1 является лучшей последовательностью преобразования, чем другая пользовательская последовательность преобразования U2 , если они содержат одну и ту же пользовательскую функцию преобразования [...] и в любом случае вторая стандартная последовательность преобразования U1 лучше, чем вторая стандартная последовательность преобразования U2.

    Следовательно, общее преобразование в F2 считается лучше, и оно выбрано.

  • Лучшая ICS для foo(double) через operator double. В итоге мы получаем две последовательности преобразований, в которых используются различные функции преобразования; ничего действительно не применяется, и мы просто получаем двусмысленность.

Ответ 2

A -> char A -> char.

A -> int A -> char -> int (поскольку char to int является продвижением и поэтому превосходит преобразование double в int).

A -> double A -> double.

Две пользовательские последовательности преобразования сопоставимы, если они включают одну и ту же пользовательскую функцию преобразования. Таким образом, A -> char является лучшей последовательностью преобразования, чем A -> int, поэтому ваш первый случай недвусмыслен. Ни A -> int, ни A -> double не лучше другого, поэтому второй случай неоднозначен.