-
Notifications
You must be signed in to change notification settings - Fork 15.2k
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.