Correct, the vptr always comes first (in Itanium ABI). This means that if you have a (void*) that "I don't know what it points at, but I know that whatever it points at is polymorphic," then you can get to the std::type_info of that mystery object via a known recipe.

Here is a simple illustration of Thiago's point:
Without knowing the definition of `B`, the compiler cannot know whether a conversion from `D*` to `B*` requires an adjustment.
Without knowing the definition of `D`, the compiler cannot know whether a conversion from `D*` to `B*` requires an adjustment.

Incidentally, John, it occurs to me that C++ already assigns a meaning to
    struct D B;
    struct D B, C;
and that this might possibly be a (weak) reason not to add meanings for
    struct D: B;
    struct D: B, C;
because of the risk of visual confusion, and possibly even wrong intuition as to what `struct D: B, C;` is supposed to mean. (Basically, does `,` bind tighter than `:`? You definitely want it to, but is this intuitively obvious to the uninitiated programmer?)
However, I readily admit that historically C++ has never let "fear of line noise" stand in the way of even the tiniest convenience feature.


On Sat, Feb 22, 2020 at 4:37 PM Thiago Macieira via Std-Proposals <> wrote:
On Saturday, 22 February 2020 12:49:36 PST John Yates via Std-Proposals wrote:
> To me a forward declaration of either of those structs as simply struct D:
> B;
> is clearly an error.  When forward declaring base classes any failure to
> match exactly the actual declaration (number, order, virtualness, etc.)
> should at the very least be UB.

That would be implied, yes.

Just to confirm without multiple or virtual inheritance:

struct B;
struct D : B;

/* later */

struct B { int i; };
struct D : B
    virtual ~D();

What is the layout of D? Two options will apply for 99% of the ABIs (64-bit

0     4     8    12     16
|  i  | pad |   vtable   |


0     4     8    12     16
|  vtable   |  i  | pad  |

In the first scenario, D and B share top-of-object address. I thought the
first case would be what the IA-64 ABI would do. But a quick test at shows that it is in fact the second case, as
you can see from:
        movq    $_ZTV1D+16, (%rax)
        movl    $42, 8(%rax)

Same for the MSVC ABI.

That is, the B-in-D sub-object is not allocated at the top of the D object.

Thiago Macieira - thiago (AT) - thiago (AT)
   Software Architect - Intel System Software Products

Std-Proposals mailing list