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

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

#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 и VC++ выбирают 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), но кажется, что только VC++ доволен кодом, в то время как 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 общий код разрешения перегрузок?


person Angelus Mortis    schedule 17.03.2016    source источник
comment
1: Дайте определение несчастному. 2: Что произойдет, если вы удалите foo(int)?   -  person Amit    schedule 17.03.2016
comment
@Amit unhappy означает, что код отклонен, а что касается удаления функции foo(int), это не является частью вопроса, вы можете попробовать это сами, пожалуйста.   -  person Angelus Mortis    schedule 17.03.2016
comment
@AngelusMortis: какую функцию выбирает VC++?   -  person davidhigh    schedule 18.03.2016


Ответы (2)


A -> char is A -> char.

A -> int равно A -> char -> int (поскольку char в int — это рекламная акция, поэтому лучше, чем преобразование из double в int).

A -> double is A -> double.

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

person T.C.    schedule 17.03.2016

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, за которым следует преобразование с плавающей запятой.

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

  1. #P5# <блочная цитата> #P6# #P7#
  2. Лучший ICS для foo(double)через operator double. В итоге мы получаем две последовательности преобразования, использующие разные функции преобразования; на самом деле ничего не применимо, и мы просто получаем двусмысленность.

person Columbo    schedule 18.03.2016