Why is there a top_offset in VTT implemented by gcc?
Asked Answered
H

1

0

Here is a detailed description of VTT in the top-voted answer.But the answer does not explain why is there a top-offset in the VTT.

From my point of view,when we down_cast a base pointer to derived pointer,the compiler already knows the offset needed to be adjusted in compile time(when there is no virtual derivation) ,so there is no need to store a top_offset in situation below:

class A {
public:
  int a;
};
class B {
public:
  int b;
  virtual void w();
};

class C : public A, public B {
public:
  int c;
};

In this case, objects of type C are laid out like this (numbers assuming 32-bit pointers):

                           +-----------------------+
                           |     0 (top_offset)    |//why?
                           +-----------------------+
c --> +----------+         | ptr to typeinfo for C |
      |  vtable  |-------> +-----------------------+
      +----------+         |         A::v()        |
      |     a    |         +-----------------------+
      +----------+         |    -8 (top_offset)    |//why?
      |  vtable  |---+     +-----------------------+
      +----------+   |     | ptr to typeinfo for C |
      |     b    |   +---> +-----------------------+
      +----------+         |         B::w()        |
      |     c    |         +-----------------------+
      +----------+

Why is there a top_offset in VTT under such situation? I think the top_offset and virtual base offset are only needed in virtual inheritance.

Habited answered 13/5, 2018 at 14:12 Comment(12)
Just google "c++ multiple inheritance top_offset" for hits.Epigraphic
@HansPassant I googled,but doesn't find an expected answer.Habited
I think this is needed for dynamic_cast if you inherit virtually from C.Schoonmaker
@Schoonmaker But the complier already knows there is no vitual inheritation,and if there is,there should also be a virtual base offset.Habited
@bigxiao There's no virtual inheritance within C. It's still possible for something else to use virtual inheritance from C, and its vtable isn't shown here.Schoonmaker
@Schoonmaker That's another question,what about the example given in the question?Habited
@bigxiao huh? The vtable still needs the same layout whether one particular part is needed by the current code or not.Schoonmaker
@Schoonmaker No,the vtt of B is different from vtt of (B in C)Habited
@bigxiao What o11c wrote was that the vtable needs the same layout, not that it needs to be same completely. The same layout is obviously needed: code using B will be compiled to use one specific layout that cannot change at runtime. And that layout includes the top offset.Sagitta
@hvd Why does the vtables need the same layout?I think it's reasonable and appropriate for B to have a different vtalble layout compared with (B in C).Habited
@bigxiao For code takes a B by reference and uses anything in the vtable, if it doesn't know whether it's plain B or B-in-C, how could it use the vtable if it couldn't know the vtable layout?Sagitta
What is A::v()?Dentist
S
2
void *top(B *b) { return dynamic_cast<void *>(b); }

There is no way for the compiler to determine at compile time what the correct offset is. This function may be called with a null pointer, a pointer to a complete B object, or a pointer to a B subobject. The three cases need to be handled differently. The offset in the vtable is what allows this to work.

Sagitta answered 15/5, 2018 at 7:45 Comment(7)
So the top_offset in vtable is used in such particular situation and thus the top_offset is needed in vtable.But in most situations is the top_offset determined directly in compile time,not by the one in vtable?Habited
@bigxiao Indeed. In those situations in which the required offset can be determined at compile time, I cannot think of any reason why a compiler would load the offset from the vtable.Sagitta
But "The offset in the vtable is what allows this to work." seems not true.It's the type_info object in vtable that allow dynamic_cast to work.With type_info one can always knows the real type of B* b and thus knows the corresponding top_offset,without the top_offset item in vtable.Habited
@bigxiao That assumes that given two types (B and C in this case), the offset of one within the other can be determined. That's typically true at compile time, but at run time you just end up needing the very same offset again. Also, in the case of the "diamond problem" (the same base class being inherited multiple times), that determination cannot be made even at compile time.Sagitta
The diamond problem is supposed to be determined at compile time.But,whatever, I aggree that it's more convenient to use the top_offset in vtable when using dynamic_cast to void* .Habited
@Dentist It was there in my comment: the same base class being inherited multiple times. If the same base class is inherited multiple times, it cannot be determined at compile time which of the B subobjects b refers to, so even with the same static type, same dynamic type, same cast target type, the adjustment could be different.Sagitta
@Dentist The adjustment for dynamic_cast<C *>(b), where C is a class inheriting B more than once.Sagitta

© 2022 - 2024 — McMap. All rights reserved.