4

Here is a piece of code which I cannot quite understand:

#include <iostream>

struct Ptr {
    void* ptr
#ifdef WAT
      = 0
#endif
    ;
};

struct A {
    Ptr ptr;
    int d;
};

struct B : public A { int e; };

static_assert(sizeof(A) == 16, "ok");
static_assert(sizeof(B) == 24, "wat");

int main() {
    // This code is just to show that the classes are actually used
    // by the program
    B b;
    b.ptr.ptr = new int{};
    b.d = b.e = 42;
    std::cout << b.ptr.ptr << " " << b.d << " " << b.e << std::endl;
}

I would expect the object sizes to be independent of the initializer for the void* ptr field. However, there is a difference whether this code is compiled with -DWAT or not, at least in clang: With -DWAT, sizeof(B) is actually 16, which is counter-intuitive to me: https://godbolt.org/z/LFuese

Is there a reasonable intuition for this behaviour? I understand that there are not many guarantees on object layouts, but is there a way to force the "default" behaviour, in the presence of the initializer?

UPDATE: It appears that the layout algorithm differs between objects that have an explicit default constructor / field initializers and such that do not (POD, plain old data type). I still don't understand the logic fully however.

Niklas B.
  • 92,950
  • 18
  • 194
  • 224
  • look like compiler bugs. can add also `Ptr() : ptr(0){}` the size of `B` will change from 24 to 16 in gcc with this – RbMm Oct 14 '18 at 17:34
  • It seems that clang exercises its freedom to put the e member into A's tail padding if A is a non-POD here, while GCC does not, according to https://stackoverflow.com/a/51334730/916657 – Niklas B. Oct 14 '18 at 17:35
  • GCC 8.1.x exhibits the same behaviour. –  Oct 14 '18 at 17:37
  • gcc do this too - https://godbolt.org/z/GqyKmg. msvc - no – RbMm Oct 14 '18 at 17:38
  • OK so GCC behaves the same if there is a default constructor. Got it. – Niklas B. Oct 14 '18 at 17:42

0 Answers0