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

问题描述 投票:8回答:2

在阅读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_;
}

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

c++ c++17 unions placement-new stdoptional
2个回答
11
投票

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

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

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

在常量表达式中有几件事你不能做,其中包括newreinterpret_cast。如果你用optional实现aligned_storage,你需要使用new创建对象和reinterpret_cast以使其恢复,这将阻止optionalconstexpr友好。

使用union实现,你没有这个问题,所以你可以在optional编程中使用constexpr(甚至在fix for trivial copyability讨论的Nicol之前,optional已经被要求可用作constexpr)。


9
投票

由于后C ++ 17缺陷修复,std::optional无法实现为对齐存储。具体来说,如果std::optional<T>是可以轻易复制的,那么T必须是可以轻易复制的。 union{empty; T t};将满足此要求

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

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

© www.soinside.com 2019 - 2024. All rights reserved.