1

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.

7
  • on vs2008, I cannot reproduce the error - and the latter test case prints 'B'. Commented Sep 11, 2015 at 11:48
  • Does foo(&c[0]) work? Commented Sep 11, 2015 at 11:49
  • 4
    Arrays are not pointers. Where does this idea come from, anyway? Also, the pointer arithmetic you need for array access isn't going to work out when you cast an array of C to pointer to B (or A, for that matter); once C contains some data members, b[1] will not give you a valid object because sizeof(B) != sizeof(C) (and it's undefined behavior before then, as well). Commented Sep 11, 2015 at 11:52
  • in foo(B*), you can know, whether it is a C by using dynamic_cast. C* c = dynamic_cast<C*>(b) in foo(B* b) and check if c is nullptr (or NULL für c++0x). If it is not nullptr, it is C. Commented Sep 11, 2015 at 12:08
  • 1
    @Gombat These classes are not polymorphic, you can't use dynamic_cast. Commented Sep 11, 2015 at 12:12

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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.

1 Comment

@sehe I got confused with how this question is related to conversions more than the fact that its C[10] is not really a polymorphic type?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.