PieterNuyts
PieterNuyts

Reputation: 579

Ambiguous overload with array passed as pointer

The following code

#include <iostream>

using namespace std;

class A {};

class B : public A {};

class C : public B {};

void foo(A *a) {
    cout << 'A' << endl;
}

void foo(B *b) {
    cout << 'B' << endl;
}

int main() {
    C *c = new C[10];

    foo(c);
}

compiles fine and prints 'B' as expected.

But when I change the main() function to

int main() {
    C c[10];

    foo(c);
}

I get a compiler error saying

test_arr.cpp: In function 'int main()':
test_arr.cpp:23:10: error: call of overloaded 'foo(C [10])' is ambiguous
test_arr.cpp:23:10: note: candidates are:
test_arr.cpp:11:6: note: void foo(A*)
test_arr.cpp:15:6: note: void foo(B*)

Why is it ambiguous now? I thought an array was always just a pointer so I don't really see the difference.

Edit:

I just realized the whole code I posted was a bad idea to start with: As soon as you add data members to the classes and access them in the foo functions you will run into problems, as explained here, since the foo(B*) function has no way of knowing it's actually dealing with C objects which potentially take more memory space. So don't do this!

Nevertheless I am still interested in understanding why the compiler complains about ambiguity here, since I really don't see a problem with that here.

Upvotes: 1

Views: 392

Answers (2)

Barry
Barry

Reputation: 302852

I believe this is a gcc bug. The code compiles on clang.

First, both candidates are viable. From [conv]:

A standard conversion sequence is a sequence of standard conversions in the following order:
— Zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion, and function-to-pointer conversion.
— Zero or one conversion from the following set: integral promotions, floating point promotion, integral conversions, floating point conversions, floating-integral conversions, pointer conversions, pointer to member conversions, and boolean conversions.

Both calls to foo would involve an array-to-pointer conversion (from C[10] to C*) and then a pointer conversion (from C* to either A* or B*). That is an allowable standard conversion.

Now, how do we rank those conversions? Amusingly, the line from the standard matches exactly our use-case, in [over.ics.rank]:

Two conversion sequences with the same rank are indistinguishable unless one of the following rules applies:
— If class B is derived directly or indirectly from class A and class C is derived directly or indirectly from B,
   — conversion of C* to B* is better than conversion of C* to A*

We have two conversions sequences of the same rank (Conversion), that are both viable, but one is considered better than the other. The code should unambiguously prefer foo(B* ) over foo(A* ). The fact that c is declared an array should not make it ambiguous.

Upvotes: 5

Tony The Lion
Tony The Lion

Reputation: 63190

C c[10] is not a polymorphic type. You might be able to get a pointer to the first item in the array, but that is still just a C.

You would get slicing if you tried to cast it to B.

C* c = new C(); can be be dynamic_cast to B.

Upvotes: 1

Related Questions