Почему этот вызов функции-члена неоднозначен?

Рассмотрим этот класс:

class Base{
public:
    void func(double a) = delete;
    void func(int a) const {}
};

int main(){
    Base base;

    base.func(1);
    return 0;
}

При компиляции с использованием clang++ возникает следующая ошибка:

clang++ --std=c++11 test.cpp 
test.cpp:22:7: error: call to member function 'func' is ambiguous
    base.func(1);

С g++ выдается предупреждение:

g++ -std=c++11 test.cpp 
test.cpp: In function ‘int main()’:
test.cpp:22:13: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: base.func(1);

Почему этот код неоднозначен?

Ответ 1

Нестатические функции-члены, как и два:

void func(double);    // #1
void func(int) const; // #2

принять также неявный параметр объекта, который рассматривается при разрешении перегрузки ([over.match]/p1) как любой другой аргумент:

Разрешение перегрузки - это механизм выбора наилучшей функции для вызова с учетом списка выражений, которые должны быть аргументами вызова, и набора функций-кандидатов, которые могут быть вызваны на основе контекста вызова. Критерии выбора для наилучшей функции - это количество аргументов, насколько аргументы соответствуют параметру-типу-списку функции-кандидата, , насколько хорошо (для нестатических функций-членов) объект соответствует параметру неявного объекта и некоторые другие свойства функции-кандидата.

После включения неявного параметра объекта в сигнатуры функций-членов компилятор видит две перегрузки:

void func(Base&, double);    // #1
void func(const Base&, int); // #2

и пытается выбрать наилучшую жизнеспособную функцию, основанную на вызове:

Base base;
base.func(1);

Преобразование из base (которое не является константой lvalue типа base) в Base& имеет ранг Точного соответствия (прямая привязка ссылок дает Идентификация) - см. Таблица 13. Преобразование из base в const Base& также имеет ранг Точного соответствия, однако [over.ics.rank]/p3.2.6 объявляет #1 лучшую последовательность преобразования:

- S1 и S2 являются привязками привязки ([dcl.init.ref]), а типы, к которым относятся ссылки, являются тот же тип, за исключением cv-квалификаторов верхнего уровня, и тип, к которому ссылается ссылка, инициализированная S2, является более cv-квалификацией, чем тип, к которому относится ссылка, инициализированная S1. [Пример:

int f(const int &);
int f(int &);
int g(const int &);
int g(int);

int i;
int j = f(i);    // calls f(int &)
int k = g(i);    // ambiguous

Теперь для второго параметра преобразование из интегрального prvalue 1 в double является преобразованием с плавающим интегралом ([conv.fpint]), которому присваивается рейтинг конверсии. С другой стороны, 1 to int - это преобразование идентичности, которое имеет ранг Точного соответствия. Для этого аргумента #2 считается лучшей последовательностью преобразования ([over.ics.rank]/p3.2.2):

- ранг S1 лучше ранга S2 или S1 и S2 имеют одинаковый ранг и различимы по правилам в параграфе ниже, или, если не это, [...]

Разрешение перегрузки для успеха требует наличия не более одного параметра, для которого разные последовательности конверсий отличаются ([over.match.best]):

Учитывая эти определения, жизнеспособная функция F1 определяется как лучшая функция, чем другая жизнеспособная функция F2, если для всех аргументов я ICS i (F1) не является худшей последовательностью преобразования, чем ICS i (F2), а затем

- для некоторого аргумента j, ICS j (F1) является лучшей последовательностью преобразования, чем ICS j (F2), или, если не это, [... ]

Здесь ICS 0 (# 1) лучше, чем ICS 0 (# 2), но, в свою очередь, ICS 1 (# 2) лучше, чем ICS 1 (# 1), поэтому компилятор не может выбирать между двумя перегрузками и обнаруживает двусмысленность.

Ответ 2

Когда функция перегружена, сначала выполняется разрешение перегрузки. Программа плохо сформирована, если удаленная функция наилучшего соответствия и выбрана.

Следовательно, ваша программа создаст ту же ошибку, что и следующая, потому что существует неявное преобразование из int в double и компилятор не знает, какую функцию вы намереваетесь вызывать:

class Base{
public:
    void func(double a) {}
    void func(int a) const {}
};

Ответ 3

Это из-за модификатора const в func(int). Экземпляр base не является константой. Компиляторы С++, похоже, сначала обнаруживают метод non-const, если экземпляр не const. И затем компиляторы обнаружили, что метод был удален. Поэтому компилятор дает предупреждение.

Попробуйте удалить модификатор const или переместите модификатор const на func(double), чтобы избавиться от предупреждения.

Кажется, что это предупреждение не о неявном преобразовании. Даже вы вызываете func на func((int)(1)) тоже не хорошо.