【问题标题】:Why is sizeof( std::variant< char > ) == 8 when using libc++ and not 2 (like with MSVC's STL and libstdc++)?为什么使用 libc++ 时 sizeof( std::variant< char > ) == 8 而不是 2(如 MSVC 的 STL 和 libstdc++)?
【发布时间】:2019-05-10 04:57:58
【问题描述】:

考虑this example on Compiler explorer

基本上,我们有这个代码sn-p:

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

但是,当使用带有 libc++ 的 clang 时,我得到了这个错误:

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

这表示sizeof( std::variant&lt; char &gt; ) == 8 在使用 libc++ 时。我已经在 Linux 上确认了这一点(请参阅上面的编译器资源管理器链接),但也可以在 Android 的 NDK r18 和 Xcode 10(iOS 和 MacOS)上使用。

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

【问题讨论】:

  • 这是质量上的差异。 libc++的质量大概可以和clang的质量媲美。还要检查访问者的丑陋实现
  • 当我为 lldb 编写 std::variant 的格式化程序并记录了 implementation of std::variant 并且它有两个成员 __index__data 我不记得为什么但 __index通常是 unsigned int`,它解释了大小 see godbolt
  • 我相信这个问题很清楚,不知道为什么反对票或投票结束。
  • @Shafik 因为他们提出了一个特定于实现的问题,并且无法通过参考标准来回答,而且 C++ 标签中许多更活跃的用户都讨厌这个问题。出于这个原因,我经常把 C++ 标签去掉。 “不清楚”的密切原因只是这些人使用的一个虚假原因,因为没有一个密切的原因真正适合。问题很明确。

标签: c++ c++17 libc++ std-variant


【解决方案1】:

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

在当前的 libc++ 中也可以使用此优化,但默认情况下似乎未启用。启用优化的宏 (_LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION) 仅在定义了 _LIBCPP_ABI_VERSION &gt;= 2_LIBCPP_ABI_UNSTABLE 时才设置。

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

https://reviews.llvm.org/D40210

【讨论】:

  • 谢谢!这确实解决了我的问题,因为在我的特定情况下我不需要 ABI 兼容性,但大小非常重要。
  • 不幸的是,这只适用于 ArchLinux(libc++ 与 clang 7 一起提供),但不适用于 Xcode 10 和 NDK r18。看起来对于 iOS、Android 和 MacOS,我将不得不坚持使用 std::variant 的巨大尺寸。
  • @DoDo 是因为 libc++ 版本还是因为它破坏了与某些预编译库(如 libc++ 本身)的二进制兼容性?我认为最近的 clang 应该使用足够新的 libc++,并且二进制兼容性问题可以通过使用宏集(可能静态链接到您的可执行文件)编译所有 C++ 库来解决,但我对这些平台没有经验。
  • @ user10605163,将 _LIBCPP_ABI_VERSION 定义为 2 并在 Android NDK r18 或 Xcode 10 (macos/ios) 上编译我的代码我收到大量编译错误,指出 std::__2::move 不存在并且类似许多其他标准功能。我的解决方案是在使用 libc++(Android、iOS、MacOS)的平台上使用mapbox variant,在其他地方使用std::variant
猜你喜欢
  • 1970-01-01
  • 2018-12-20
  • 2022-01-16
  • 1970-01-01
  • 2021-09-26
  • 2012-08-18
  • 1970-01-01
  • 1970-01-01
  • 2017-02-22
相关资源
最近更新 更多