VF1
VF1

Reputation: 1652

Casting with multiple inheritance in C++

The following code does not compile, and gcc -std=c++11 says it is an invalid static cast:

class A { public: virtual ~A() {} };
class B { public: virtual ~B() {} };
class AD : public A { public: virtual ~AD() {} };

class AB : public B, public A {};
class ADB : public B, public AD {};

int main() {
    ADB adb;
    ADB* ptr = &adb;
    AB* cast = static_cast<AB*>(ptr);
    return 0;
}

I would like to cast a class of type ADB to AB. This feels like it should be safe to do - after all, the memory structure of ADB is B followed by AD, which itself is just A followed by AD's own members. Thus it would seem that literally forcing compiler to interpret the pointer ptr as an AB would always be defined:

class A { public: virtual ~A() {} };
class B { public: virtual ~B() {} };
class AD : public A { public: virtual ~AD() {} };

class AB : public B, public A {};
class ADB : public B, public AD {};

int main() {
    ADB adb;
    ADB* ptr = &adb;
    AB* cast = reinterpret_cast<AB*>(ptr);
    return 0;
}

This does not compile either, resulting in the linker complaining about undefined references to the vtable and to operator delete(void*).

So clearly at some point the vtable pointers are not "syncing up" when casted, in which case I somehow need to cast the individual base class of AD of ADB to just the A of AB. I'm not exactly sure about that, just guessing. How can I do this or something equivalent to it?

Upvotes: 1

Views: 133

Answers (2)

Zenilogix
Zenilogix

Reputation: 1393

You can only successfully cast between classes which are in the object's inheritance structure. So, you can cast an ADB to an A, a B, or an AD. Your rationalization of similar memory layouts does not apply. While an understanding of the underlying implementation can offer helpful insights into certain "how"s and "why"s (especially when it comes to performance), you should not ever have to consider the underlying implementation to understand (or rationalize) the rules of language features. It works the way it does to help you express higher-level intent.

More importantly, you should never circumvent restrictions built in to the language based on exploited knowledge of the underlying implementation. While there may be occasional valid cases to do so, most of the time, it will result in non-portable code or code that doesn't survive an update of the compiler - failing to compile or breaking mysteriously in production.

Anyway, your assumption about memory layout may be incorrect; defining the virtual destructors is forcing the compiler to inject vtable pointers into the memory layouts of A,B, and AD, so the actual memory layout of an ADB may not be what you think it is.

Upvotes: 1

Stephane Rolland
Stephane Rolland

Reputation: 39896

If you want to cast an AB, a static_cast could only cast an AB to a B or an A. That's all.

This is the guaranty that offers static_cast at compilation time: It only allows cast operations that are logically ok, between types that are related.

You could also imagine these static_cast, and they would be ok:

  • an A in AD
  • a B or an A in AB
  • a B or an AD in ADB
  • an A in ADB

But there is NO path in the inheritance hierarchy between AB and ADB to do what you want. In no way AB and ADB are related types.

Upvotes: 0

Related Questions