литерал '0', являющийся допустимым кандидатом для int и const string & overloads, вызывает неоднозначный вызов
Я недавно исправил ошибку.
В следующем коде одна из перегруженных функций была константной, а другая - нет. Проблема будет исправлена, если сделать обе функции константными.
У меня вопрос, почему компилятор жаловался на это только тогда, когда параметр был 0.
#include <iostream>
#include <string>
class CppSyntaxA
{
public:
void f(int i = 0) const { i++; }
void f(const std::string&){}
};
int main()
{
CppSyntaxA a;
a.f(1); // OK
//a.f(0); //error C2666: 'CppSyntaxA::f': 2 overloads have similar conversions
return 0;
}
Ответы
Ответ 1
0
является особенным в C++. Нулевой указатель имеет значение 0
поэтому C++ разрешит преобразование 0
в тип указателя. Это значит, когда вы звоните
a.f(0);
Вы можете вызывать void f(int я = 0) const
с int
со значением 0
, или вы можете вызывать void f(const std::string&)
с void f(const std::string&)
char*
инициализированным нулевым значением.
Обычно версия int
будет лучше, так как это точное совпадение, но в этом случае версия int
является const
, поэтому она требует "преобразования" a
в const CppSyntaxA
, где версия std::string
не требует такого преобразования, но делает требуется преобразование в char*
а затем в std::string
. Это считается достаточным изменением в обоих случаях, чтобы считаться равным преобразованием и, следовательно, неоднозначным. Если обе функции будут const
или const
это решит проблему, и будет выбрана перегрузка int
, поскольку она лучше.
Ответ 2
У меня вопрос, почему компилятор жаловался на это только тогда, когда параметр был 0.
Потому что 0 - это не только целочисленный литерал, но и литерал нулевого указателя. 1 не является литералом нулевого указателя, поэтому нет никакой двусмысленности.
Неоднозначность возникает из неявного конструктора преобразования std::string
который принимает указатель на символ в качестве аргумента.
Теперь преобразование идентичности из int в int в противном случае было бы предпочтительнее преобразования из указателя в строку, но есть другой аргумент, который включает преобразование: неявный объектный аргумент. В одном случае это преобразование из CppSyntaxA&
в CppSyntaxA&
то время как в другом случае это CppSyntaxA&
для const CppSyntaxA&
.
Таким образом, одна перегрузка предпочтительна из-за одного аргумента, а другая перегрузка предпочтительна из-за другого аргумента, и, таким образом, не существует однозначно предпочтительной перегрузки.
Проблема будет исправлена, если сделать обе функции константными.
Если обе перегрузки квалифицированы как const
, то последовательность преобразования неявных аргументов объекта идентична, и поэтому одно из перегрузок однозначно предпочтительнее.