【问题标题】:std::optional implemented as union vs char[]/aligned_storagestd::optional 实现为 union vs char[]/aligned_storage
【发布时间】:2019-02-19 15:18:28
【问题描述】:

在阅读 GCC 对 std::optional 的实现时,我注意到了一些有趣的事情。我知道boost::optional的实现如下:

template <typename T>
class optional {
    // ...
private:
    bool has_value_;
    aligned_storage<T, /* ... */> storage_;
}

但是 libstdc++libc++(以及 Abseil)都实现了它们的 optional 类型,如下所示:

template <typename T>
class optional {
    // ...
private:
    struct empty_byte {};
    union {
        empty_byte empty_;
        T value_;
    };
    bool has_value_;
}

在我看来,它们在功能上是相同的,但是使用其中一个有什么优势吗? (除了后者明显没有放置 new ,这真的很好。)

【问题讨论】:

    标签: c++ c++17 unions placement-new stdoptional


    【解决方案1】:

    在我看来,它们在功能上是相同的,但是使用其中一个有什么优势吗? (除了后者明显缺少新的位置,这真的很好。)

    这不仅仅是“非常好” - 它对于一个非常重要的功能至关重要,即:

    constexpr std::optional<int> o(42);
    

    在常量表达式中不能做几件事,包括newreinterpret_cast。如果您使用aligned_storage 实现optional,则需要使用new 创建对象并使用reinterpret_cast 将其取出,这将阻止optionalconstexpr 友好。

    使用union 实现,您就没有这个问题,因此您可以在constexpr 编程中使用optional(甚至在Nicol 所说的fix for trivial copyability 之前,optional 已经需要作为constexpr 使用)。

    【讨论】:

      【解决方案2】:

      std::optional不能实现为对齐存储,由于 C++17 后的缺陷修复。具体来说,如果T 可简单复制,则std::optional&lt;T&gt; 必须可简单复制。 union{empty; T t}; 将满足此要求

      内部存储和放置-new/delete 不能使用。在 C++ 内存模型中,将 TriviallyCopyable 对象的字节复制到尚不包含对象的存储空间不足以实际创建该对象。相比之下,在 TriviallyCopyable 类型上使用的union 的编译器生成的副本将是微不足道的,并将用于创建目标对象。

      所以std::optional必须以这种方式实现。

      【讨论】:

      • 为什么将 TriviallyCopyable 对象的字节副本复制到未初始化的存储不会创建该对象?在我看来,大多数原始类型都应该这样做,你能举个例子说明为什么不这样做吗?
      • @RonMordechai:因为 [intro.object]/1 这么说。或者更确切地说,它列出了创建对象的句法结构,而“复制内存”不在其中。因此,它不能创建对象。内存中的值不会自行产生对象。这是“有效”的 UB 的常见情况。
      • 为什么需要空字段?它可以简单地与union { T t; }; 一起工作吗?
      • @random:因为T 可能不是默认可构造的。或可分配。当optional 没有T 时,必须以没有T 的方式完成。如果你有一个包含单个元素的联合,它总是存储该类型的元素。
      • @NicolBolas 你的意思是如果联合只有一个成员,它将自动构建?我刚刚在 Visual Studio 中进行了实验,并且没有调用构造函数。所以看起来 union 默认不存储任何对象。
      猜你喜欢
      • 2014-11-08
      • 1970-01-01
      • 1970-01-01
      • 2013-03-05
      • 2021-10-27
      • 2013-06-05
      • 2013-10-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多