Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

operator[](const char *) ambiguity

Tags:

c++

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?

like image 263
6502 Avatar asked Aug 04 '14 07:08

6502


1 Answers

§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.

like image 199
T.C. Avatar answered Oct 27 '22 00:10

T.C.