To give a little bit more context:
C and C++ require that every field in a structure has a unique pointer value (unless explicitly annotated in C++20) and C requires that a pointer to the first field of an object is equivalent to a pointer to the object.
This combination of constraints first means that `s3_0` in this example will report a size of 1, but that byte is purely padding. When we lay out `s3_1`, we have a first field that is one byte (purely of padding) followed by 7 bytes of padding to correctly align `b`. This adds up to 8 bytes of padding.
No one would write that by hand deliberately, but you can encounter things like this when you use std::conditional_t in a C++ class to do something like:
struct Placeholder {};
template<typename T = void>
struct SomeClass
{
std::conditional_t<std::is_same_v<T, void>, T, PlaceHolder> extra_data;
long unconditional_field;
...
};
This pattern is quite useful for classes where you want to provide an extension point for carrying some extra state but still support the case where the instantiating code doesn't want to provide any. Prior to C++20 there was no standard way of ensuring that the placeholder didn't take up any space. If you happened to pick the first field of the object for this field then you could end up with something equivalent to Craig's example, where you have 8 bytes of padding at the start of `SomeClass` before `unconditional_field`.
In C++20, you can write this:
struct Placeholder {};
template<typename T = void>
struct SomeClass
{
[[no_unique_address]]
std::conditional_t<std::is_same_v<T, void>, T, PlaceHolder> extra_data;
long unconditional_field;
...
};
Now, if `extra_data` gets the type `PlaceHolder`, `(char*)&extra_data == (char*)&unconditional_Field`. That's typically what you want, because any accesses to `extra_data` will be guarded by a `constexpr if` that checks that `T` is not `void` and so the field will not actually be referenced at all in instantiations where it is not used to carry extra state.
David