Consider the following two struct
s whose sizes are 8 and 1 bytes respectively:
class eight {
int i;
char c;
eight(const blub&) {}
};
class one {
char s;
one(const blob&) {}
};
When they are embedded in another struct like this:
struct example1 {
eight b0;
one b1;
};
The sizeof(example1)
will be 12 bytes because b0
and b1
are stored in non-overlapping storage (9 bytes) and then the 4-byte alignment requirement of eight
rounds that up to 12. See it on godbolt.
C++20 introduces the no_unique_address
attribute, which allows adjacent empty members to share the same address. It also explicitly allows the scenario described above of using padding of one member to store another. From cppreference:
Indicates that this data member need not have an address distinct from all other non-static data members of its class. This means that if the member has an empty type (e.g. stateless Allocator), the compiler may optimise it to occupy no space, just like if it were an empty base. If the member is not empty, any tail padding in it may be also reused to store other data members.
If we use [[no_unique_address]]
on the first member (eight
), the size drops to 8 bytes.
struct example2 {
[[no_unique_address]] eight b0;
one b1;
};
However, if we put it on the second member, the size is still 12 bytes (see it on godbolt):
struct example3 {
eight b0;
[[no_unique_address]] one b1;
};
Why?
Intuitively I would expect it to either be required on both ("both overlapped members must opt-in") or on either one ("anyone can opt in an then overlapping is allowed"), but not for it work on one and not the other.