In the following example struct E
inherits structs C
and D
and has no other data members:
struct A{};
struct B{};
struct C : A, B {};
struct D : A, B {};
struct E : C, D {};
int main() {
static_assert(sizeof(C) == 1);
static_assert(sizeof(D) == 1);
//static_assert(sizeof(E) == 2); // in Clang and GCC
static_assert(sizeof(E) == 3); //in MSVC
}
In all compilers I have tested, sizeof(C)==1
and sizeof(D)==1
, and only in MSVC sizeof(E)==3
so more than the summed size of its parents/members, demo: https://gcc.godbolt.org/z/aEK7rjKcW
Actually I expected to find sizeof(E) <= sizeof(C) sizeof(D)
(less in case empty base optimization). And there is hardly any padding here, otherwise sizeof(E)
would be 2 or 4.
What is the purpose of extra space (sizeof(E)-sizeof(C)-sizeof(D) == 1
) in E
?
CodePudding user response:
First, it can be larger than the sum of sub-objects due to padding and alignment. However, you're probably aware of that, and that's not what you are asking.
To determine the layout in your case, you can print the offsets of all the sub-objects (and the sizes of their types) using the following code:
static E x;
int main() {
E *e = &x;
C *c = e;
D *d = e;
A *ca = c, *da = d;
B *cb = c, *db = d;
#define OFF(p) printf(#p " %d %d\n", (int)((char*)p - (char*)e), (int)sizeof(*p))
OFF(e);
OFF(c);
OFF(ca);
OFF(cb);
OFF(d);
OFF(da);
OFF(db);
}
The output for gcc/clang is:
e 0 2
c 0 1
ca 0 1
cb 0 1
d 1 1
da 1 1
db 1 1
The output for MSVC is:
e 0 3
c 0 1
ca 0 1
cb 1 1
d 2 1
da 2 1
db 3 1
This indicates that the way MSVC implements EBO is different from other compilers. In particular, instead of placing A
and B
at the same address within C
, and at the same address within D
(like other compilers do), it put them at different offsets. Then, even though sizeof(C) == 1
, it allocates the full two bytes for it when it is a sub-object. This is most likely done so to avoid cb
aliasing some other B
from another sub-object, even though it wouldn't be a problem in this scenario.