This example seems to compile with VC10 and gcc (though my version of gcc is very old).
EDIT: R. Martinho Fernandez tried this on gcc 4.7 and the behaviour is still the same.
struct Base
{
operator double() const { return 0.0; }
};
struct foo
{
foo(const char* c) {}
};
struct Something : public Base
{
void operator[](const foo& f) {}
};
int main()
{
Something d;
d["32"];
return 0;
}
But clang complains:
test4.cpp:19:6: error: use of overloaded operator '[]' is ambiguous (with operand types 'Something' and 'const char [3]')
d["32"]
~^~~~~
test4.cpp:13:10: note: candidate function
void operator[](const foo& f) {}
^
test4.cpp:19:6: note: built-in candidate operator[](long, const char *)
d["32"]
^
test4.cpp:19:6: note: built-in candidate operator[](long, const restrict char *)
test4.cpp:19:6: note: built-in candidate operator[](long, const volatile char *)
test4.cpp:19:6: note: built-in candidate operator[](long, const volatile restrict char *)
The overload resolution is considering two possible functions from looking at this expression:
- calling Something::operator[] (after a user defined conversion)
- calling built in operator for const char* (think “32”[d]) (after a user defined conversion and standard conversion double to long).
If I had written d["32"] as d.operator[]("32"), then overload resolution won’t even look at option 2, and clang will also compile fine.
EDIT: (clarification of questions)
This seems to be a complicated area in overload resolution, and because of that I’d appreciate very much answers that explain in detail the overload resolution in this case, and cite the standard (if there’s some obscure/advanced likely to be unknown rule).
If clang is correct, I’m also interested in knowing why the two are ambiguous / one is not preferred over another. The answer likely would have to explain how overload resolution considers implicit conversions (both user defined and standard conversions) involved on the two candidates and why one is not better than the other.
Note: if operator double() is changed to operator bool(), all three (clang, vc, gcc) will refuse to compile with similar ambiguous error.
It should be easier to picture why the overload resolution is ambiguous by going through it step-by-step.
§13.5.5 [over.sub]Now, we first need an overload set. That’s constructed according to
§13.3.1and contains member aswell as non-member functions. See this answer of mine for a more detailed explanation.§13.3.1 [over.match.funcs]Then, an argument list is constructed:
And then the argument list is tested against every member of the overload set:
Then, since we found out that there are implicit conversions required, we take a look at
§13.3.3 [over.match.best] p1:Now let’s construct those implicit conversion sequences for
f1andf2in the overload set (§13.3.3.1):§13.3.3.2 [over.ics.rank] p2So
ICS1(f1)is better thanICS1(f2)andICS2(f1)is worse thanICS2(f2).Conversely,
ICS1(f2)is worse thanICS1(f1)andICS2(f2)is better thanICS2(f1).§13.3.3 [over.match.best]Well, f*ck. 🙂 As such, Clang is correct in rejecting that code.