Why no ambiguity in this function call? - c++

I'm wondering why there's no ambiguity in this function call:
#include <iostream>
#include <vector>
template <class T>
class C
{
public:
typedef char c;
typedef double d;
int fun() {}
static c testFun( decltype(&C::fun) ) {return c();}
static d testFun(...) { return d(); }
};
int main() {
C<int>::testFun(0); // Why no ambiguity?
}
http://coliru.stacked-crooked.com/a/241ce5ab82b4a018

There's a ranking of implicit conversion sequences, as defined in [over.ics.rank], emphasis mine:
When comparing the basic forms of implicit conversion sequences...
- a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion
sequence or an ellipsis conversion sequence, and
- a user-defined conversion sequence (13.3.3.1.2) is a better conversion sequence than an ellipsis conversion
sequence (13.3.3.1.3).
So we have two functions:
static char testFun( int (C::*)() ) { return char(); }
static double testFun( ... ) { return double(); }
Both functions are viable for testFun(0). The first would involve a "null member pointer conversion" as per [conv.mem], and is a standard conversion sequence. The second would match the ellipsis and be an ellipsis conversion sequence. By [over.ics.rank], the former is preferred. There's no ambiguity, the one is strictly better than the other.
An ambiguous overload would arise if we had two equivalent conversion sequences that the compiler could not decide between. Consider if we had something like:
static char testFun(int* ) { return 0; }
static int testFun(char* ) { return 0; }
testFun(0);
Now both overloads would be equivalent as far as the conversion sequences go, so we'd have two viable candidates.

You have a standard conversion vs an ellipsis conversion. The standard says that a standard conversion is a better conversion sequence than the latter. [over.ics.rank]/p2:
a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion
sequence or an ellipsis conversion sequence
A pointer-to-member conversion is a standard conversion sequence. 0 is a null pointer constant and can be converted to a pointer-to-member. [conv.mem]/p1:
A null pointer constant (4.10) can be converted to a pointer to member type; the result is the null member
pointer value of that type and is distinguishable from any pointer to member not created from a null pointer
constant. Such a conversion is called a null member pointer conversion.
Therefore the first overload is preferred.

13.3.2 Viable functions
A candidate function having fewer than m parameters is viable only if it has an ellipsis in its parameter list (8.3.5). For the purposes of overload resolution, any argument for which there is no corresponding parameter is considered to “match the ellipsis” (13.3.3.1.3).
And also
Viable functions
Given the set of candidate functions, constructed as described above, the next step of overload resolution is examining arguments and parameters to reduce the set to the set of viable functions
To be included in the set of viable functions, the candidate function must satisfy the following:
1) If there are M arguments, the candidate function that has exactly M parameters is viable
2) If the candidate function has less than M parameters, but has an ellipsis parameter, it is viable.
[...]
Overloading resolution

A null pointer literal 0 should be an exact match to a function accepting a function pointer and treated as stronger than matching anything (...).

Related

Why is "operator bool()" getting called if a class object needs conversion to int in C++?

I have a function call to some_func that takes int as argument.
int some_func(int);
class S {
public:
S(int v) {
a = v;
}
...
operator bool() const {
return true;
}
int a;
}; // class S doesn't define any "operator int"
S obj;
int x = some_func(obj); // some_func expected an int argument
In the above code, some_func expected int argument but is called with an object of type S. So it needs to convert it to "int".
But why is it using "operator bool"? Shouldn't it produce compile error saying no correct conversion method to int is specified for class S?
If I remove operator bool definition, then the program doesn't compile and gives an error about argument type mismatch in some_func call.
C++ has implicit conversions. By defining the conversion operator operator bool() you have made S implicitly convertible to bool. This is a user defined conversion. A conversion can consist of a sequence of conversions (only one of which may be user defined).
While there is no direct conversion from S to int, there is a built-in conversion from bool to int (this is an integer promotion, true is converted to 1 and false to 0). Therefore the conversion sequence S -> bool -> int is valid and therefore S is implicitly convertible to int.
PS. If you wanted to prevent the implicit conversion from bool when calling that function, you could declare a deleted overload that would be preferred by the overload resolution:
int some_func(bool) = delete;
Bool and Int both are data-types. Data types can be converted either implicitly or explicitly. Implicit conversions are not visible to the user.
When you are defining:
operator bool() const {
return true;
}
Then the S is implicitly converted to bool. You can explicitly convert it to int. But note that while explicit conversion keep in mind the range of bytes and don't convert larger size data-types to smaller otherwise you will loose data as some bytes (larger datatype - smaller datatype) would be truncated.
In your problem the integers are converted to boolean(implicit conversion) Here the numbers other than 0 will be stored as true that is in the number form as 1 and the number 0 itself would be trated as false and hence will be stored as 0.
You can prevent this implicit conversion by defining your own explicit operator. See here or you can declare a delete overload.
See here for similar problem. I guess you will learn something there in the solutions that you seeking to.
Whenever the compiler might need to convert an expression to another type, in particular for each argument of a function call, it attempts:
An optional standard conversion sequence.
An optional user-defined conversion.
An optional standard conversion sequence.
A standard conversion sequence is one defined by the language, like arithmetic-to-arithmetic conversions, pointer conversion, etc. A user-defined conversion is one involving a constructor that can take one argument or a conversion function like your S::operator bool().
So in your example, it succeeds by finding no standard conversion, followed by your S::operator bool() user-defined conversion, followed by the standard conversion from bool to int.
For more gory details, see the cppreference page on implicit conversions.
A boolean is internally represented as an integer.
true is defined to be 1 (basically everything except 0) and false is defined to be 0.
So it is possible to do something like this:
if(true+false+true) will result in if(2) which will be true since only if(0) would be false.

In overload resolution, does selection of a function that uses the ambiguous conversion sequence necessarily result in the call being ill-formed?

The question arose while I was researching the answer to this SO question. Consider the following code:
struct A{
operator char() const{ return 'a'; }
operator int() const{ return 10; }
};
struct B {
void operator<< (int) { }
};
int main()
{
A a;
B b;
b << a;
}
The conversion of a to int can be either via a.operator char() followed by an integral promotion, or a.operator int() followed by an identity conversion (i.e., no conversion at all). The standard says that (§13.3.3.1 [over.best.ics]/p10, footnote omitted, bolding mine; all quotes are from N3936):
If several different sequences of conversions exist that each convert
the argument to the parameter type, the implicit conversion sequence
associated with the parameter is defined to be the unique conversion
sequence designated the ambiguous conversion sequence. For the
purpose of ranking implicit conversion sequences as described in
13.3.3.2, the ambiguous conversion sequence is treated as a user-defined sequence that is indistinguishable from any other
user-defined conversion sequence. If a function that uses the
ambiguous conversion sequence is selected as the best viable function,
the call will be ill-formed because the conversion of one of the
arguments in the call is ambiguous.
Here, B::operator<<(int) is the only viable candidate - and hence is the best viable candidate, even though the conversion sequence for the parameter is the ambiguous conversion sequence. According to the bolded sentence, then, the call should be ill-formed because "the conversion of one of the arguments in the call is ambiguous".
Yet no compiler that I tested (g++, clang, and MSVC) actually reports an error, which makes sense because after the function to call is selected through overload resolution, the function's "parameter (8.3.5) shall be initialized (8.5, 12.8, 12.1) with its corresponding
argument" (§5.2.2 [expr.call]/p4). This initialization is copy-initialization (§8.5 [dcl.init]/p15), and according to §8.5 [dcl.init]/p17, results in a new round of overload resolution to determine the conversion function to use:
The semantics of initializers are as follows. The destination type
is the type of the object or reference being initialized and the
source type is the type of the initializer expression. If the initializer is not a single (possibly parenthesized) expression, the
source type is not defined.
[...]
If the destination type is a (possibly cv-qualified) class type: [...]
Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered. The applicable conversion
functions are enumerated (13.3.1.5), and the best one is chosen
through overload resolution (13.3). The user-defined conversion so
selected is called to convert the initializer expression into the
object being initialized. If the conversion cannot be done or is
ambiguous, the initialization is ill-formed.
[...]
And in this round of overload resolution, there is a tiebreaker in §13.3.3 [over.match.best]/p1:
a viable function F1 is defined to be a better function than another
viable function F2 if for all arguments i, ICSi(F1) is not a worse
conversion sequence than ICSi(F2), and then
for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,
the context is an initialization by user-defined conversion (see 8.5, 13.3.1.5, and 13.3.1.6) and the standard conversion sequence from the return type of F1 to the destination type (i.e., the type of the
entity being initialized) is a better conversion sequence than the
standard conversion sequence from the return type of F2 to the
destination type.
(Example and remainder of the list omitted)
Since the standard conversion sequence from int to int (Exact Match rank) is better than the standard conversion sequence from char to int (Promotion rank), the first beats the second, and there should be no ambiguity - the conversion defined by operator int() will be used for the initialization, which then contradicts the sentence in §13.3.3.1 [over.best.ics]/p10 that says the function call will be ill-formed because of ambiguity.
Is there anything wrong in the above analysis, or is that sentence a bug in the standard?
When deciding the best user-defined conversion for a user-defined conversion sequence we have an overload candidate set.
§13.3.3/p1 says:
Define ICSi(F) as follows:
[...]
let ICSi(F) denote the implicit conversion sequence that converts the i-th argument in the list to the
type of the i-th parameter of viable function F. 13.3.3.1 defines the implicit conversion sequences and
13.3.3.2 defines what it means for one implicit conversion sequence to be a better conversion sequence
or worse conversion sequence than another.
Given these definitions, a viable function F1 is defined to be a better function than another viable function
F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
— [...]
— the context is an initialization by user-defined conversion (see 8.5, 13.3.1.5, and 13.3.1.6) and the
standard conversion sequence from the return type of F1 to the destination type (i.e., the type of the
entity being initialized) is a better conversion sequence than the standard conversion sequence from
the return type of F2 to the destination type.
This applies since
§13.3.3.1.2/p2
2 The second standard conversion sequence converts the result of the user-defined conversion to the target
type for the sequence. Since an implicit conversion sequence is an initialization, the special rules for
initialization by user-defined conversion apply when selecting the best user-defined conversion for a user-defined
conversion sequence (see 13.3.3 and 13.3.3.1).
and therefore the conversion sequence involving operator int is selected as best match.
Finally I would rephrase §13.3.3.1/p10 as
If several different sequences of conversions exist that each convert the argument to the parameter type and it was not possible to determine a best candidate,
the implicit conversion sequence associated with the parameter is defined to be the unique conversion sequence
designated the ambiguous conversion sequence. For the purpose of ranking implicit conversion sequences
as described in 13.3.3.2, the ambiguous conversion sequence is treated as a user-defined sequence that is
indistinguishable from any other user-defined conversion sequence134. If a function that uses the ambiguous
conversion sequence is selected as the best viable function, the call will be ill-formed because the conversion
of one of the arguments in the call is ambiguous.

operator[](const char *) ambiguity

The following code
#include <string>
struct Foo {
operator double() {
return 1;
}
int operator[](std::string x) {
return 1;
}
};
int main() {
Foo()["abcd"];
}
Compiles fine with g++ but fails with clang and intel compilers because of an ambiguity between the declared method and native operator [].
It would be clear for me if Foo had an implicit conversion to int, but here the conversion is to double. Doesn't that solve the ambiguity?
§13.3.3.1.2 [over.ics.user]/p1-2:
A user-defined conversion sequence consists of an initial standard
conversion sequence followed by a user-defined conversion (12.3)
followed by a second standard conversion sequence. If the user-defined
conversion is specified by a constructor (12.3.1), the initial
standard conversion sequence converts the source type to the type
required by the argument of the constructor. If the user-defined
conversion is specified by a conversion function (12.3.2), the initial
standard conversion sequence converts the source type to the implicit
object parameter of the conversion function.
The second standard conversion sequence converts the result of the
user-defined conversion to the target type for the sequence.
In particular, there's an implicit conversion from floating point to integral type (§4.9 [conv.fpint]/p1):
A prvalue of a floating point type can be converted to a prvalue of an
integer type. The conversion truncates; that is, the fractional part
is discarded. The behavior is undefined if the truncated value cannot
be represented in the destination type.
For overload resolution purposes, the applicable candidates are:
Foo::operator[](std::string x) // overload
operator[](std::ptrdiff_t, const char *); // built-in
Given an argument list of types (Foo, const char [5]).
To match the first operator function, the first argument is an exact match; the second requires a user-defined conversion.
To match the second built-in function, the first argument requires a user-defined conversion sequence (the user-defined conversion to double followed by a standard conversion to std::ptrdiff_t, a floating-integral conversion). The second argument requires a standard array-to-pointer conversion (still exact match rank), which is better than a user-defined conversion.
Thus for the first argument the first function is better; for the second argument the second function is better, we have a criss-cross situation, overload resolution fails, and the program is ill-formed.
Note that, while for the purposes of operator overload resolution, a user-defined conversion sequence can have two standard conversion sequences (one before and one after the user-defined conversion), and operands of non-class-type can be converted to match the candidates, if a built-in operator is selected, the second standard conversion sequence is not applied for operands of class type, and no conversion at all is applied for operands for non-class type before the operator is interpreted as a built-in (§13.3.1.2 [over.match.oper]/p7):
If a built-in candidate is selected by overload resolution, the
operands of class type are converted to the types of the corresponding
parameters of the selected operation function, except that the second
standard conversion sequence of a user-defined conversion sequence
(13.3.3.1.2) is not applied. Then the operator is treated as the
corresponding built-in operator and interpreted according to Clause 5.
Thus if Foo::operator[](std::string x) is removed, the compiler should report an error, though clang doesn't. This is an obvious clang bug, as it fails to reject the example given in the standard.

Distinguishing between user-defined conversion sequences by the initial standard conversion sequence

The standard appears to provide two rules for distinguishing between implicit conversion sequences that involve user-defined conversion operators:
c++11
13.3.3 Best viable function [over.match.best]
[...] a viable function F1 is defined to be a better function than another viable function
F2 if [...]
the context is an initialization by user-defined conversion (see 8.5, 13.3.1.5, and 13.3.1.6) and the
standard conversion sequence from the return type of F1 to the destination type (i.e., the type of the
entity being initialized) is a better conversion sequence than the standard conversion sequence from
the return type of F2 to the destination type.
13.3.3.2 Ranking implicit conversion sequences [over.ics.rank]
3 - Two implicit conversion sequences of the same form are indistinguishable conversion sequences unless one of
the following rules applies: [...]
User-defined conversion sequence U1 is a better conversion sequence than another user-defined conversion sequence U2 if they contain the same user-defined conversion function or constructor or aggregate
initialization and the second standard conversion sequence of U1 is better than the second standard
conversion sequence of U2.
As I understand it, 13.3.3 allows the compiler to distinguish between different user-defined conversion operators, while 13.3.3.2 allows the compiler to distinguish between different functions (overloads of some function f) that each require a user-defined conversion in their arguments (see my sidebar to Given the following code (in GCC 4.3) , why is the conversion to reference called twice?).
Are there any other rules that can distinguish between user-defined conversion sequences? The answer at https://stackoverflow.com/a/1384044/567292 indicates that 13.3.3.2:3 can distinguish between user-defined conversion sequences based on the cv-qualification of the implicit object parameter (to a conversion operator) or of the single non-default parameter to a constructor or aggregate initialisation, but I don't see how that can be relevant given that that would require comparison between the first standard conversion sequences of the respective user-defined conversion sequences, which the standard doesn't appear to mention.
Supposing that S1 is better than S2, where S1 is the first standard conversion sequence of U1 and S2 is the first standard conversion sequence of U2, does it follow that U1 is better than U2? In other words, is this code well-formed?
struct A {
operator int();
operator char() const;
} a;
void foo(double);
int main() {
foo(a);
}
g++ (4.5.1), Clang (3.0) and Comeau (4.3.10.1) accept it, preferring the non-const-qualified A::operator int(), but I'd expect it to be rejected as ambiguous and thus ill-formed. Is this a deficiency in the standard or in my understanding of it?
The trick here is that converting from a class type to a non-class type doesn't actually rank any user-defined conversions as implicit conversion sequences.
struct A {
operator int();
operator char() const;
} a;
void foo(double);
int main() {
foo(a);
}
In the expression foo(a), foo obviously names a non-overloaded non-member function. The call requires copy-initializing (8.5p14) the function parameter, of type double, using the single expression a, which is an lvalue of class type A.
Since the destination type double is not a cv-qualified class type but the source type A is, the candidate functions are defined by section 13.3.1.5, with S=A and T=double. Only conversion functions in class A and any base classes of A are considered. A conversion function is in the set of candidates if:
It is not hidden in class A, and
It is not explicit (since the context is copy-initialization), and
A standard conversion sequence can convert the function's return type (not including any reference qualifiers) to the destination type double.
Okay, both conversion functions qualify, so the candidate functions are
A::operator int(); // F1
A::operator char() const; // F2
Using the rules from 13.3.1p4, each function has the implicit object parameter as the only thing in its parameter list. F1's parameter list is "(lvalue reference to A)" and F2's parameter list is "(lvalue reference to const A)".
Next we check that the functions are viable (13.3.2). Each function has one type in its parameter list, and there is one argument. Is there an implicit conversion sequence for each argument/parameter pair? Sure:
ICS1(F1): Bind the implicit object parameter (type lvalue reference to A) to expression a (lvalue of type A).
ICS1(F2): Bind the implicit object parameter (type lvalue reference to const A) to expression a (lvalue of type A).
Since there's no derived-to-base conversion going on, these reference bindings are considered special cases of the identity conversion (13.3.3.1.4p1). Yup, both functions are viable.
Now we have to determine if one implicit conversion sequence is better than the other. This falls under the fifth sub-item in the big list in 13.3.3.2p3: both are reference bindings to the same type except for top-level cv-qualifiers. Since the referenced type for ICS1(F2) is more cv-qualified than the referenced type for ICS1(F1), ICS1(F1) is better than ICS1(F2).
Therefore F1, or A::operator int(), is the most viable function. And no user-defined conversions (with the strict definition of a type of ICS composed of SCS + (converting constructor or conversion function) + SCS) were even to be compared.
Now if foo were overloaded, user-defined conversions on the argument a would need to be compared. So then the user-defined conversion (identity + A::operator int() + int to double) would be compared to other implicit conversion sequences.

Should this compile? Overload resolution and implicit conversions

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]
Thus, a subscripting expression x[y] is interpreted as x.operator[](y) for a class object x of type T if T::operator[](T1) exists and if the operator is selected as the best match function by the overload resolution mechanism (13.3.3).
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]
p2 The set of candidate functions can contain both member and non-member functions to be resolved against the same argument list. So that argument and parameter lists are comparable within this heterogeneous set, a member function is considered to have an extra parameter, called the implicit object parameter, which represents the object for which the member function has been called. [...]
p3 Similarly, when appropriate, the context can construct an argument list that contains an implied object argument to denote the object to be operated on.
// abstract overload set (return types omitted since irrelevant)
f1(Something&, foo const&); // linked to Something::operator[](foo const&)
f2(std::ptrdiff_t, char const*); // linked to operator[](std::ptrdiff_t, char const*)
f3(char const*, std::ptrdiff_t); // linked to operator[](char const*, std::ptrdiff_t)
Then, an argument list is constructed:
// abstract argument list
(Something&, char const[3]) // 'Something&' is the implied object argument
And then the argument list is tested against every member of the overload set:
f1 -> identity match on argument 1, conversion required for argument 2
f2 -> conversion required for argument 1, conversion required for argument 2 (decay)
f3 -> argument 1 incompatible, argument 2 incompatible, discarded
Then, since we found out that there are implicit conversions required, we take a look at §13.3.3 [over.match.best] p1:
Define ICSi(F) as follows:
if F is a static member function, [...]; otherwise,
let ICSi(F) denote the implicit conversion sequence that converts the i-th argument in the list to the type of the i-th parameter of viable function F. 13.3.3.1 defines the implicit conversion sequences and 13.3.3.2 defines what it means for one implicit conversion sequence to be a better conversion sequence or worse conversion sequence than another.
Now let's construct those implicit conversion sequences for f1 and f2 in the overload set (§13.3.3.1):
ICS1(f1): 'Something&' -> 'Someting&', standard conversion sequence
ICS2(f1): 'char const[3]' -> 'foo const&', user-defined conversion sequence
ICS1(f2): 'Something&' -> 'std::ptrdiff_t', user-defined conversion sequence
ICS2(f2): 'char const[3]' -> 'char const*', standard conversion sequence
§13.3.3.2 [over.ics.rank] p2
a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence.
So ICS1(f1) is better than ICS1(f2) and ICS2(f1) is worse than ICS2(f2).
Conversely, ICS1(f2) is worse than ICS1(f1) and ICS2(f2) is better than ICS2(f1).
§13.3.3 [over.match.best]
p1 (cont.) Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then [...]
p2 If there is exactly one viable function that is a better function than all other viable functions, then it is the one selected by overload resolution; otherwise the call is ill-formed.
Well, f*ck. :) As such, Clang is correct in rejecting that code.
It seems there is no question that both Something::operator[](const foo& f) and the built-in operator[](long, const char *) are viable candidate functions (13.3.2) for overload resolution. The types of real arguments are Something and const char*, and implicit conversion sequences (ICFs) I think are:
for Something::operator[](const foo& f): (1-1) identity conversion, and (1-2) foo("32") through foo::foo(const char*);
for operator[](long, const char *): (2-1) long(double(d)) through Something::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),
a viable function F1 is defined to be a better function than another viable function
F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), ...
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:
13.6
Built-in operators
[over.built]
The candidate operator functions that represent the built-in operators defined in Clause 5 are specified in
this subclause. These candidate functions participate in the operator overload resolution process as described
in 13.3.1.2 and are used for no other purpose. [ Note: Because built-in operators take only operands with
non-class type, and operator overload resolution occurs only when an operand expression originally has class
or enumeration type, operator overload resolution can resolve to a built-in operator only when an operand
has a class type that has a user-defined conversion to a non-class type appropriate for the operator, or when
an operand has an enumeration type that can be converted to a type appropriate for the operator. Also note
that some of the candidate operator functions given in this subclause are more permissive than the built-in
operators themselves. As described in 13.3.1.2, after a built-in operator is selected by overload resolution
the expression is subject to the requirements for the built-in operator given in Clause 5, and therefore to
any additional semantic constraints given there. If there is a user-written candidate with the same name
and parameter types as a built-in candidate operator function, the built-in operator function is hidden and
is not included in the set of candidate functions. — end note ]
:
For every cv-qualified or cv-unqualified object type T there exist candidate operator functions of the form
T& operator[](T *, std::ptrdiff_t);
T& operator[](std::ptrdiff_t, T *);
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:
void Something::operator[](const foo& f)
operator[](long, const char *)
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:
void operator[](const char *a) { return (*this)[foo(a)]; }
to class Something