noisy cat
noisy cat

Reputation: 3065

Derived** to Base**

Consider following code:

#include <iostream>

using namespace std;

class Base
{
public:
    int foo;
};

class Derived : public Base
{
public:
    float bar;
};

int main()
{
    Base** baseArr = new Base*[30];

    for (int i=0; i<30; i++)
    {
        Derived* derived = new Derived();
        derived->foo = i;
        derived->bar = i * 2.5;
        baseArr[i] = derived;
    }

    //Notice this!
    Derived** derivedArr = (Derived**)(baseArr);

    for (int i=0; i<30; i++)
        cout << "My Base " << i << ": " << derivedArr[i]->foo << ", " << derivedArr[i]->bar << endl;

    return 0;
}

Is it safe to do this array to array cast? The pointer size is same across the program, so it sounds like I won't get any padding errors. However, I know that the correct way to do this is to iterate through each element and cast it individually.

However, Im trying to utilize this kind of cast to move my template public functions implementations to .cpp file by using non-generic private function returning array, so I can be sure that my Base array will contain only specific Derived pointers.

private:
    Base** Find(function<bool(Base*)> Predicate, int& N); //Implemented in .CPP

public:
    template <class T> T** FindByType(int &N) //T is derived from Base
    {
        //Safe?
        return (T**)(Find(
            [](Base* b)->bool { return typeid(T) == typeid(b); },
            N));
    };

This is just a simplified example, of course. I've got number of reasons to use RTTI in this case. N is used to controll array size.

I am wondering if this unsafe cast will fail with multi-inheritance, like for example Derived class would also inherit OtherBase and I'd like to cast to OtherBase**, I'd also like to know if by any chance I reach undefined behaviour with this cast, or any potential problems I may encounter if I decide to use this construct.

Upvotes: 0

Views: 132

Answers (2)

Hatted Rooster
Hatted Rooster

Reputation: 36483

No, this is unsafe.

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

  • the dynamic type of the object,
  • a cv-qualified version of the dynamic type of the object,
  • a type similar (as defined in 4.4) to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
  • a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
  • a char or unsigned char type.

Shamelessly stolen from Ben

None of those cases is true in the case of casting Base** to Derived**.

Upvotes: 1

Sam Varshavchik
Sam Varshavchik

Reputation: 118350

No it is not safe.

A pointer to Derived is not the same thing as a pointer to Base. A pointer to Derived can be converted to a pointer to Base, but the end result is a different pointer.

And because a pointer to Derived is not the same thing as a pointer to Base, a pointer to a pointer to Derived is also not the same thing as a pointer to a pointer to Base.

Upvotes: 2

Related Questions