Reputation: 579
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
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
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