Downcasting in C++ with virtual base class

Viewed 1328

Consider the scenario:

class Base { };

class Derived: public virtual Base {};

int main(void) {
    Derived * d = new Derived();

    Base * b = d;

    Derived * x = static_cast<Derived*>(b); // Probably impossible

    delete x;
}

Here the downcast from pointer to Base to pointer to Derived is giving a compilation error saying "cannot convert since base is virtual". I cannot dynamic cast here since Base is not a polymorphic type. My questions are:

  1. Is this simply impossible to do?
  2. How does the C++ standard (11 or 14) state that this is impossible?

If it is impossible, I want to understand why. I know that virtual methods are implemented via vptr table, but how are virtual base classes implemented? I also know that the C++ standard does not define implementation, but I would like to understand the implementation of something common like gcc none the less. So the question is:

  1. What is the underlying implementation of a standard compiler like gcc which makes this feature impossible?

This is probably a fairly simple question for all the C++ gurus out there, but I just could not find anything concrete after google-ing for half an hour. Please feel free to duplicate, down-vote etc if this is already covered.

1 Answers

Is this simply impossible to do?

Yes.

How does the C++ standard (11 or 14) state that this is impossible?

It is stated under the requirements of static_cast.

[expr.static.cast] - emphasis mine

11 A prvalue of type “pointer to cv1 B,” where B is a class type, can be converted to a prvalue of type “pointer to cv2 D,” where D is a class derived (Clause [class.derived]) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists ([conv.ptr]), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is neither a virtual base class of D nor a base class of a virtual base class of D.

As you can see, Base cannot be virtual or even a non-virtual base of another virtual base.

What is the underlying implementation of a standard compiler like gcc which makes this feature impossible?

Virtual inheritance is a mechanism that allows the same Base subobject to be shared between different intermediate base classes. For instance, if you were to add:

class Derived2: public virtual Base {};
class MostDerived: public Derived, public Derived2 {};

Then MostDerived will have a sub-object of type Derived and type Derived2, but unlike regular inheritance, they won't each have their own Base sub-object (thus 2 Base sub-objects in MostDerived). Instead there will be only one Base sub-object, and both Derived and Derived2 will refer to it.

This is commonly accomplished by Derived and Derived2 accessing the Base indirectly through some pointer. They cannot rely on it being placed inside themselves at a certain offset. The location of their Base is determined by the most derived object. So if you have a Base*, there is no one well-determined way to go to the Derived object that refers to it.

In the case of non-virtual inheritance, the Base would be located at a place known to the compiler relative to the Derived sub-object that contains it fully. So the compiler could do the pointer arithmetic required to obtain the Derived sub-object. But in your case, it cannot, because there is no guarantee of relative placement. The access is (generally) indirect.


You didn't ask, but I might as well address that too. The reason a dynamic_cast works for polymorphic classes, is that polymorphic classes can have RTTI associated with them in their vtable (if they have one). And because that Base* will point to the vtable of MostDerived (or Derived), the dynamic_cast mechanism can leverage information written in that table to figure out how to obtain the sub-object it needs. The table can contain all the offset information it needs to get from any one sub-object to any other sub-object. And because it is the table of the most derived object (at run-time), that information is fixed and reliable.

Related