C ++ overload resolution and constant

(all tests are done in Microsoft (R) C / C ++ Compiler Optimization version 19.00.24215.1 for x86)

consider this minimal example:

struct myString
{
    operator const char *( ) const { return &dummy; }
    char& operator[]( unsigned int ) { return dummy; }
    const char& operator[]( unsigned int ) const { return dummy; }

    char dummy;
};

int main()
{
    myString str;
    const char myChar = 'a';

    if( str[(int) 0] == myChar ) return 0; //error, multiple valid overloads
}

      

according to overload resolution rules (from cppreference)

F1 is defined as a better function than F2 if the implicit conversions for all F1 arguments are not worse than the implicit conversions for all F2 arguments and

1) there is at least one argument F1 whose implicit conversion is better than the corresponding implicit conversion for this argument F2

2) or. if not, (only in the context of initializing non-classification by conversion), the standard conversion sequence from the return type F1 to the type is initialized better than the standard conversion sequence from the return type F2

char& operator[]( unsigned int )

should be better, according to 1).

Of the two arguments (this = myString) does not need to be converted at all, but operator const char *( ) const

converts it to const char * and const char& operator[]( unsigned int ) const

converts it to const myString, so there is one argument without any implicit conversion, which turns out to be the best conversion

However, my compiler yells the following error:

1>  [///]\sandbox\sandbox\sandbox.cpp(29): error C2666: 'myString::operator []': 3 overloads have similar conversions
1>  [///]\sandbox\sandbox\sandbox.cpp(19): note: could be 'const char &myString::operator [](unsigned int) const'
1>  [///]\sandbox\sandbox\sandbox.cpp(18): note: or       'char &myString::operator [](unsigned int)'
1>  [///]\sandbox\sandbox\sandbox.cpp(29): note: while trying to match the argument list '(myString, int)'

      

also note that using if( str[0u] == myChar ) return 0;

or remove operator const char *( ) const

to resolve the error

Why is there an error here and that I am wrong about the overload resolution rules?

edit: it could be a visual C ++ error in this version, any final confirmation on this?

+3


source to share


1 answer


Here's a cited version of the problem that reproduces on all the compilers I've thrown at it .

#include <stddef.h>

struct myString
{
    operator char *( );
    char& operator[]( unsigned ptrdiff_t );
};

int main()
{
    myString str;
    if( str[(ptrdiff_t) 0] == 'a' ) return 0; //error, multiple valid overloads
}

      

Basically, you have two candidate functions to get char

for bool operator==(char,char)

: [over.match.oper] / 3

Note that if it myString::operator[]

took ptrdiff_t

instead unsigned ptrdiff_t

, it would hide the inline operator behind [over.Built] / 1 . So if all you want to do is avoid these kinds of problems, just make sure that any overload operator[]

that accepts an integer value accepts ptrdiff_t

.

I'll skip the viability check [over.match.viable] and go directly to the conversion rankings.

char& myString::operator[]( unsigned ptrdiff_t )

For overloading, it is considered to have a leading implicit object parameter, so the signature to be matched is (myString&, unsigned ptrdiff_t)

myString&

=> myString&

Standard conversion sequence: Identity (Rank: Exact match) - link directly linked

ptrdiff_t

=> unsigned ptrdiff_t

Standard conversion sequence: Value Conversion <-> Cumulative conversion (Rank: Conversion) - signed lvalue for unsigned prvalue

char& operator[]( char*, ptrdiff_t)

myString&

=> char*

Custom Conversion Funnel : Identity + operator char*(myString&)

Note that for [over.match.oper] / 7 we do not get the second standard conversion sequence.

ptrdiff_t

=> ptrdiff_t



Standard conversion sequence: Identification (Rank: Exact match)

Best Viable Function

First argument

Standard conversion sequence is better than custom conversion sequence ( [over.ics.rank] /2.1 )

Second argument

Conversion Rank is worse than Rank Exact Match ( [over.ics.rank] /3.2.2 )

Result

We cannot satisfy the requirement

if for all arguments i ICSi (F1) is not a worse conversion sequence than ICSi (F2)

therefore none of the features are the best feature.

Hence per [over.match.best] / 2 is ambiguous .


How to fix it?

Well, the easiest solution is never to have the parameter overload operator[]

something that could be converted to ptrdiff_t

something other than an exact match. When looking at a lookup table , this means that you should always declare your member function operator[]

as X& T::operator[]( ptrdiff_t )

. This covers the usual "Act like Array" use case. As noted above, usage ptrdiff_t

will surely suppress even candidate searches by operator T*

forcing the inline indexing operator out of the table.

Another option is that for the class, both are not defined T1 operator[]

, and operator T2*

where T1

and T2

can perform the same parameter of the (possibly implicit) function call. This goes for the cases where you use operator[]

smart syntactic things and get things like T T::operator[](X)

. If X::operator ptrdiff_t()

exists, for example, etc. T::operator T*()

then you are ambiguous again.

The only use-case T::operator T*()

I can imagine is if you want your type to implicitly convert to a pointer to itself, like a function. Do not do this...

+1


source







All Articles