The answer lies in what a cast to base pointer actually does in C++. Consider the following simple example:
struct A { std::uint32_t a; };
struct B { std::uint32_t b; };
struct D : A, B { std::uint32_t d; };
Here D
has two bases, A
and B
. But only one of them can live at the beginning of D
(lets say it is A
), so when you convert the D
to the other one (B
), the value of the pointer needs to change, to point at some point in the middle of D
.
To visualize this, consider how an object of type D
looks like in your memory:
0 1 2 3 4 5 6 7 8 9 A B
[ A::a ][ B::b ][ D::d ]
^ a D* would point here
^ a A* would point here
^ a B* must point here
Whilst D
expects all three of these integers, when thinking of this object as an A
, we only expect the first one, but the pointer is (numerically) the same, we only expect a shorter object. However, when thinking of it as an B
, we need to point at the one integer in the middle.
When you do that to one pointer, the compiler will take care of this for you by changing the value of the pointer appropriately. However, when you do that to an array of pointers, the compiler would need to emit a loop over all elements of that and correct every single pointer - when in fact it cannot even know where the array ends (since the compiler only sees a pointer to its beginning)!
Therefore C++ disallows this kind of conversion.