为什么在使用 libc++ 时 sizeof( std::variant< char > ) == 8 而不是 2(就像 MSVC 的 STL 和 libstdc++)?

Why is sizeof( std::variant< char > ) == 8 when using libc++ and not 2 (like with MSVC's STL and libstdc++)?

考虑 this example on Compiler explorer

基本上,我们有这个代码片段:

#include <cstdint>
#include <variant>

enum class Enum1 : std::uint8_t { A, B };

enum class Enum2 : std::uint8_t { C, D };

using Var = std::variant< Enum1, Enum2 >;
using Var2 = std::variant< char >;

template< std::size_t s >
struct print_size;

void func() {
    print_size< sizeof( Var ) >{};
    print_size< sizeof( Var2 ) >{};
}

如果我们使用 GCC 的 libstdc++(使用 clang 或 GCC)编译它,我们会得到预期的编译错误:

error: implicit instantiation of undefined template 'print_size<2>'

此外,与 MSVC 类似(如预期):

error C2027: use of undefined type 'print_size<2>'

但是,当将 clang 与 libc++ 一起使用时,出现此错误:

error: implicit instantiation of undefined template 'print_size<8>'

表示使用libc++时sizeof( std::variant< char > ) == 8。我已经在 Linux 上(参见上面的编译器资源管理器 link)以及 Android 的 NDK r18 和 Xcode 10(iOS 和苹果操作系统)。

libc++ 的 std::variant 实现使用这么多内存是否有原因,或者这只是 libc++ 中的错误并且应该报告给 libc++ 开发人员?

原因似乎是在 libc++ 的原始 std::variant 实现中,总是使用 unsigned int 来存储 std::variant 的活动类型索引,而 libstdc++ 选择最小的无符号整数能够存储最大索引的类型。

目前libc++也有这个优化,但是好像默认没有启用。启用优化的宏 (_LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION) 仅在定义 _LIBCPP_ABI_VERSION >= 2_LIBCPP_ABI_UNSTABLE 时设置。

我想,因为原始实现没有进行此优化,并且由于数据布局更改而破坏了 std::variant 在两个方向上的兼容性,因此默认情况下未启用它以保持二进制兼容性旧版本。可以通过设置提到的 ABI 版本宏来启用较新的 ABI,但是当然所有库也需要使用这个新的 ABI 版本进行编译。

https://reviews.llvm.org/D40210