literal `0` being a valid candidate for int and const string& overloads causes ambiguous call

0 is special in C++. A null pointer has the value of 0 so C++ will allow the conversion of 0 to a pointer type. That means when you call

a.f(0);

You could be calling void f(int i = 0) const with an int with the value of 0, or you could call void f(const std::string&) with a char* initialized to null.

Normally the int version would be better since it is an exact match but in this case the int version is const, so it requires "converting" a to a const CppSyntaxA, where the std::string version does not require such a conversion but does require a conversion to char* and then to std::string. This is considered enough of a change in both cases to be considered an equal conversion and thus ambiguous. Making both functions const or non const will fix the issue and the int overload will be chosen since it is better.


My question is why compiler only complained about it when the parameter was 0.

Because 0 is not only an integer literal, but it is also a null pointer literal. 1 is not a null pointer literal, so there is no ambiguity.

The ambiguity arises from the implicit converting constructor of std::string that accepts a pointer to a character as an argument.

Now, the identity conversion from int to int would otherwise be preferred to the conversion from pointer to string, but there is another argument that involves a conversion: The implicit object argument. In one case, the conversion is from CppSyntaxA& to CppSyntaxA& while in other case it is CppSyntaxA& to const CppSyntaxA&.

So, one overload is preferred because of one argument, and the other overload is preferred because of another argument and thus there is no unambiguously preferred overload.

The issue will be fixed by making both functions const.

If both overloads are const qualified, then the implicit object argument conversion sequence is identical, and thus one of the overloads is unambiguously preferred.