std :: optional作为union vs char []/aligned_storage实现

Ron*_*Ron 9 c++ placement-new unions c++17 stdoptional

在阅读GCC的实现时,std::optional我发现了一些有趣的东西.我知道boost::optional实现如下:

template <typename T>
class optional {
    // ...
private:
    bool has_value_;
    aligned_storage<T, /* ... */> storage_;
}
Run Code Online (Sandbox Code Playgroud)

但是libstdc ++libc ++(以及Abseil)都实现了optional这样的类型:

template <typename T>
class optional {
    // ...
private:
    struct empty_byte {};
    union {
        empty_byte empty_;
        T value_;
    };
    bool has_value_;
}
Run Code Online (Sandbox Code Playgroud)

他们看起来因为它们在功能上是相同的,但使用一个优于另一个有什么优势吗?(除了明显缺乏后者的新位置,这真的很好.)

Bar*_*rry 12

他们看起来因为它们在功能上是相同的,但使用一个优于另一个有什么优势吗?(除了明显缺乏后者的新位置,这真的很好.)

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

constexpr std::optional<int> o(42);
Run Code Online (Sandbox Code Playgroud)

在常量表达式中有几件事你不能做,那些包括newreinterpret_cast.如果实施optionalaligned_storage,你就需要使用new创建对象,并reinterpret_cast把它找回来了,这将防止optionalconstexpr友好.

通过union实现,您没有这个问题,因此您可以optionalconstexpr编程中使用(甚至在修复之前,Nicol正在讨论的简单可复制性,optional已经被要求可用constexpr).


Nic*_*las 11

std::optional 由于后C++ 17缺陷修复,无法实现为对齐存储.具体而言,std::optional<T>如果可以轻易地复制,则需要是可以T轻易复制的.A union{empty; T t};将满足此要求

内部存储和放置new/ delete使用不能.在C++内存模型中,从TriviallyCopyable对象执行字节复制到尚未包含对象的存储是不够的,无法实际创建该对象.相比之下,编译器生成的unionTriviallyCopyable类型的副本将是微不足道的,并且将用于创建目标对象.

所以std::optional 必须以这种方式实施.

  • @RonMordechai:因为 [intro.object]/1 是这么说的。或者更确切地说,它列出了创建对象的语法结构,而“复制内存”并不在其中。因此,它无法创建对象。内存中的值本身不会产生对象。这是 UB“有效”的常见情况。 (2认同)
  • 为什么需要空字段?它可以简单地与 ``union { T t; 一起使用吗?};```? (2认同)