Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpectedly ambiguous overload resolution in VC++ 2012

Visual C++ 2012. Code. I think it should compile; the compiler respectfully disagrees. I've narrowed my repro down to:

struct  B { };

void foo(B* b, signed int si) { } // Overload 1
void foo(B const* b, unsigned int ui) { } // Overload 2

int main()
{
    B b;
    unsigned int ui;
    foo(&b, ui);
}

So we've got two candidates for overload resolution. For the first overload, the first argument exactly matches, and the second argument requires an integral conversion (unsigned to signed). For the second overload, the second argument exactly matches, and the first argument requires a cv-adjustment (because &b is a pointer to non-const).

Now, it seems that this should be entirely unambiguous. For Overload 1, the first argument is an "Exact Match" as defined by the standard's section on overload resolution, but the second is a "Conversion". For Overload 2, both arguments are "Exact Matches" (qualification conversion being at the same rank as identity). Therefore (my apparently imperfect reasoning goes), Overload 2 should be chosen, with no ambiguity. And yet:

a.cpp(12): error C2666: 'foo' : 2 overloads have similar conversions
    a.cpp(6): could be 'void foo(const B *,unsigned int)'
    a.cpp(5): or       'void foo(B *,int)'
    while trying to match the argument list '(B *, unsigned int)'
    note: qualification adjustment (const/volatile) may be causing the ambiguity

GCC seems fine with the code, both in default dialect and in C++11 (thanks, IDEOne!). So I'm kiiiiind of inclined to chalk this up to a bug in MSVC, but (a) you know what they say about people who think their bugs are compiler bugs, and (b) this seems like it would be a pretty obvious bug, the sort that would have sent up red flags during their conformance testing.

Is this a noncompliant MSVC, or a noncompliant GCC? (Or both?) Is my reasoning regarding the overload resolution sound?

like image 674
Sneftel Avatar asked Sep 02 '14 13:09

Sneftel


1 Answers

MSVC is correct.

gcc 4.9.0 says:

warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: [enabled by default]

clang 3.4.1 agrees that the two functions are ambiguous.

Although B* => B* and B* => B const* both have Exact Match rank, the former is still a better conversion sequence per over.ics.rank/3; this is (per example) to ensure that:

int f(const int *);
int f(int *);
int i;
int j = f(&i); // calls f(int*)

From over.ics.rank/3:

Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if [...]
— S1 and S2 differ only in their qualification conversion and yield similar types T1 and T2 (4.4), respectively, and the cv-qualification signature of type T1 is a proper subset of the cv-qualification signature of type T2. [...]

And, of course, unsigned int => unsigned int is better than unsigned int => signed int. So of the two overloads, one has a better implicit conversion sequence on the first argument, and the other has a better implicit conversion sequence on the second argument. So they cannot be distinguished per over.match.best/1.

like image 111
ecatmur Avatar answered Nov 04 '22 21:11

ecatmur