Given the following program:
#include <cstdio>
#include <type_traits>
#include <utility>
struct EmptyClass {};
template <typename T, typename U>
struct Storage {
protected:
union Union {
T t;
U u;
} data;
char flag = false;
};
class AnotherEmptyClass {};
template <typename T, typename U>
class Derived : private Storage<T, U>, public AnotherEmptyClass {};
static_assert(std::is_standard_layout_v<Derived<char, EmptyClass>>);
int main() {
printf("Storage<char, EmptyClass>: %zu\n", sizeof(Storage<char, EmptyClass>));
printf("Derived<char, EmptyClass>: %zu\n", sizeof(Derived<char, EmptyClass>));
printf("Storage<char, char>: %zu\n", sizeof(Storage<char, char>));
printf("Derived<char, char>: %zu\n", sizeof(Derived<char, char>));
}
this outputs 2 2 2 2 on Linux, but 2 3 2 2 on Windows (both with clang and MSVC).
Why is that? It seems that using EmptyClass as a member of the union prevents empty-base class optimization on the derived class. But for a standard-layout type, empty base class optimization is required. Or is there a different reason for this layout?