Skip to content

[libc++] std::variant introduces padding if a variant member contains a variant #129803

Open
@zygoloid

Description

@zygoloid

Testcase:

#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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    libc++libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions