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.

choxsword
  • 3,187
  • 18
  • 44
  • 3
    Just google "c++ multiple inheritance top_offset" for hits. – Hans Passant May 13 '18 at 14:40
  • 1
    @HansPassant I googled,but doesn't find an expected answer. – choxsword May 13 '18 at 15:02
  • I *think* this is needed for `dynamic_cast` if you inherit virtually from `C`. – o11c May 13 '18 at 15:20
  • @o11c But the complier already knows there is no vitual inheritation,and if there is,there should also be a `virtual base offset`. – choxsword May 13 '18 at 15:26
  • @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. – o11c May 13 '18 at 15:27
  • @o11c That's another question,what about the example given in the question? – choxsword May 14 '18 at 12:21
  • 1
    @bigxiao huh? The vtable still needs the same layout whether one particular part is needed by the current code or not. – o11c May 14 '18 at 16:16
  • @o11c No,the vtt of B is different from vtt of (B in C) – choxsword May 15 '18 at 01:15
  • 1
    @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. –  May 15 '18 at 04:56
  • @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). – choxsword May 15 '18 at 05:19
  • 2
    @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? –  May 15 '18 at 07:40
  • What is `A::v()`? – curiousguy Jun 24 '18 at 19:31

1 Answers1

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.

  • 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? – choxsword May 15 '18 at 11:47
  • @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. –  May 15 '18 at 11:52
  • 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. – choxsword May 15 '18 at 11:55
  • @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. –  May 15 '18 at 12:06
  • 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*` . – choxsword May 15 '18 at 12:11
  • @curiousguy 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. –  Jun 24 '18 at 19:49
  • @curiousguy The adjustment for `dynamic_cast(b)`, where `C` is a class inheriting `B` more than once. –  Jun 24 '18 at 21:01