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 seems there is no question that both
Something::operator[](const foo& f)
and the built-inoperator[](long, const char *)
are viable candidate functions (13.3.2) for overload resolution. The types of real arguments areSomething
andconst char*
, and implicit conversion sequences (ICFs) I think are:Something::operator[](const foo& f)
: (1-1) identity conversion, and (1-2)foo("32")
throughfoo::foo(const char*)
;operator[](long, const char *)
: (2-1)long(double(d))
throughSomething::operator double() const
(inherited from Base), and (2-2) identity conversion.Now if we rank these ICFs according to (13.3.3.2), we can see that (1-1) is a better conversion than (2-1), and (1-2) is a worse conversion than (2-2). According to the definition in (13.3.3),
Therefore, neither of the considered two candidate functions is better than the other one, and thus the call is ill-formed. I.e. Clang appears to be correct, and the code should not compile.
It would seem from 13.6 in the C++11 spec that clang is correct here:
edit
Once you get past which operator functions exist, this just becomes standard overload resolution as described by section 13.3 of the standard -- about 10 pages of details, but the gist of it is that for a function call to not be ambiguous, there needs to be a single function that is at least as good a match as all the possible, viable functions on every argument, and a better match than the others on at least one argument. There's a lot of spec detail on exactly what 'better' means, but it boils down to (in this case) a match not requiring any user-defined conversion operator or object constructor is better than one which does.
So in this case, there are two viable matches:
The first is a better match for the first argument, while the second is a better match for the second. So unless there's some other function that is better than both of these, its ambiguous.
That latter point is a possble workaround -- add:
to class Something
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.1
and 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
f1
andf2
in the overload set (§13.3.3.1
):§13.3.3.2 [over.ics.rank] p2
So
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.