Question

Let's assume I have a Base class and a Derived class:

class Base {};

class Derived : public Base {};

I fun I can now obviously assign a pointer to Derived to the contents of myBase as follows:

void fun( Base** myBase ) {

    Derived* myDerived = new Derived();
    *myBase = myDerived;
}

int _tmain(int argc, _TCHAR* argv[])
{
    Base *myBase = NULL;
    fun( &myBase );

    return 0;
}

Now, the problem is that I want to assign to an array of Base* (I cannot use a typedef to mask the three-star since the signature of fun is auto-generated):

void fun( Base*** myBase ) {

    Derived** myDerived = new Derived*();
    *myBase = myDerived;
}

Why do I get C2440: '=' : cannot convert from 'Derived **' to 'Base **' while the conversion from Derived* to Base* is perfectly fine?

Was it helpful?

Solution

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.

OTHER TIPS

Rule of pointer conversion is governed as laid down in the standard

N3337 : 4.10 Pointer conversions [conv.ptr]

In case of conversion from a pointer to derived to a pointer to base as you have depicted in your examaple

Derived* myDerived = new Derived();
*myBase = myDerived;

is valid because the standard says

A prvalue of type “pointer to cv D”, where D is a class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class (Clause 10) of D. If B is an inaccessible (Clause 11) or ambiguous (10.2) base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion is a pointer to the base class subobject of the derived class object. The null pointer value is converted to the null pointer value of the destination type.

But in case of conversion from

'Derived **' to 'Base **'

It is not a supported prvalue conversion.

To summarize the section, a prvalue of Type T1 can be converted to a prvalue of Type T2, iff

  1. T1 is a null pointer constant that evaluates to zero or a prvalue of type std::nullptr_t i.e. assigning NULL or nullptr to a pointer type.
  2. T1 is of type “pointer to cv T,” where T is an object type, and T2 is of type “pointer to cv void” i.e. T *pT = new T(); void *p = pT
  3. T1 is of type “pointer to cv D”, where D is a class type, and T2 is of type “pointer to cv B”, where B is a base class of D i.e. Base* pBase = new Derived()

You cannot do that because it would create uncheckable restrictions. When you dereference myBase, you'd get a Derived**, which semantically shouldn't allow you to assign it a Base again. This causes real issues when you use multiple inheritance.

If this many indirections are necessary, your solution would be to use a Base** instead. Remember that you can still assign a Derived to **myBase once you get there.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top