Search code examples
c++virtual-inheritance

Why static upcast with virtual inheritance is always correct for GCC?


After learnt from : Why can't static_cast be used to down-cast when virtual inheritance is involved?

I'm expecting following code give me the result that shows the static_cast is wrong and dynamic_cast is right.

#include <stdio.h>
class A {
 public:
  virtual ~A() {}
  int a;
};

class B : public virtual A {
  int b;
};

class C : public virtual A {
  int c;
};

class D : public B, public C {
  int d;
};

int main() {
  D obj;
  A* a1 = &obj;
  A* a2 = (A*)&obj;
  A* a3 = dynamic_cast<A*>(&obj);
  A* a4 = static_cast<A*>(&obj);

  C* c = &obj;
  A* a5 = c;
  A* a6 = (A*)(c);
  A* a7 = dynamic_cast<A*>(c);
  A* a8 = static_cast<A*>(c);

  B* b = &obj;
  A* a9 = b;
  A* a10 = (A*)b;
  A* a11 = dynamic_cast<A*>(b);
  A* a12 = static_cast<A*>(b);

  printf("D: %llx %llx %llx %llx %llx\n", &obj, a1, a2, a3, a4);
  printf("C: %llx %llx %llx %llx %llx\n", c, a5, a6, a7, a8);
  printf("B: %llx %llx %llx %llx %llx\n", b, a9, a10, a11, a12);
}

However, gcc 8/9 gives me following reuslt, showing both static cast and dynamic cast is correct:

D: 7ffddb098a80 7ffddb098aa0 7ffddb098aa0 7ffddb098aa0 7ffddb098aa0
C: 7ffddb098a90 7ffddb098aa0 7ffddb098aa0 7ffddb098aa0 7ffddb098aa0
B: 7ffddb098a80 7ffddb098aa0 7ffddb098aa0 7ffddb098aa0 7ffddb098aa0

So what's the magic involved here? How can I make a case that reproduce the static_cast problem?

Note: gcc8 with -fdump-lang-class gives me following output:

Class D
   size=48 align=8
   base size=32 base align=8
D (0x0x7f0756155000) 0
    vptridx=0 vptr=((& D::_ZTV1D) + 24)
  B (0x0x7f0755f834e0) 0
      primary-for D (0x0x7f0756155000)
      subvttidx=8
    A (0x0x7f075614c0c0) 32 virtual
        vptridx=40 vbaseoffset=-24 vptr=((& D::_ZTV1D) + 104)
  C (0x0x7f0755f83548) 16
      subvttidx=24 vptridx=48 vptr=((& D::_ZTV1D) + 64)
    A (0x0x7f075614c0c0) alternative-path

Solution

  • Not sure, but it seems like you confuse upcasting with downcasting. In your code there are only upcasts, and those are fine. You get the expected compiler error for example with this:

      D obj;
      A* a4 = static_cast<A*>(&obj);
      D* d = static_cast<D*>(a4);
    

    gcc reports:

    <source>: In function 'int main()':
    <source>:26:28: error: cannot convert from pointer to base class 'A' to pointer to derived class 'D' because the base is virtual
       26 |   D* d = static_cast<D*>(a4);
          |                            ^
    

    If you remove the virtual inheritance in your example the static_cast would fail as well, because then the cast is ambiguous.