Description
#include <variant>
struct A {
int x;
};
struct B {
int y;
int z;
};
static_assert(sizeof(B) == 8);
static_assert(sizeof(std::variant<A, B>) == 12);
struct C {
std::variant<int> v;
};
static_assert(sizeof(C) == 8);
static_assert(sizeof(std::variant<A, C>) == 16);
variant<A, C>
ought to be only 12 bytes, but is actually 16 bytes. The reason for this is that std::variant
derives from __sfinae_ctor_base<...>
and __sfinae_assign_base<...>
, and those base classes are the same for std::variant<A, C>
and for std::variant<int>
.
This prevents the variant's first field (the __union
) from being put at offset 0 within the variant, because that would mean we have two different __sfinae_ctor_base<...>
subobjects at the same offset within the same object, and the C++ language rules don't permit that struct layout.
The solution is to change variant
so that it doesn't derive from a class that is, or can be, independent of the variant
's template arguments. Perhaps either change the __sfinae_...
types to use CRTP (even though they don't care what the derived class is), or remove them and rely on getting the special members' properties from the __impl
type instead.
Of course, fixing this will break std::variant
's ABI, so it'd need to be done only in the unstable ABI. :(
std::optional
appears to use the same implementation strategy, so I would imagine it has the same deficiency (assuming it puts the T
first, not the bool
), but I've not checked. And it looks like std::tuple
may also suffer from the same issue.