constexpr std::optional 可能的实现

Ziz*_*Tai 5 c++ c++11 c++14 c++17

我试图实现std::optionalconstexpr支持的做法。用法类似于:

constexpr optional<int> x(123);
int arr[*x];
Run Code Online (Sandbox Code Playgroud)

在尝试实现这一点时,我遇到了一个无法解决的问题:在optional<T>对象内部,我使用一个std::aligned_storage_t<sizeof (T), alignof (T)>对象来存储值,并使用 new inoptional<T>的构造函数将值构造到存储中。但是placement new 不能在constexpr构造函数中使用:

constexpr optional(const T& value)
    noexcept(std::is_nothrow_copy_constructible<T>::value)
    : ...
{
    new (ptr_to_storage) T(value);  // this breaks `constexpr`
}
Run Code Online (Sandbox Code Playgroud)

我还能如何实现这一点?

Chr*_*eck 5

你可以使用工会。

看看 Andrzej 是如何做到的:

https://github.com/akrzemi1/Optional/blob/master/optional.hpp#L282

template <class T>
union storage_t
{
    unsigned char dummy_;
    T value_;

    constexpr storage_t( trivial_init_t ) noexcept : dummy_() {};

    template <class... Args>
    constexpr storage_t( Args&&... args ) : value_(constexpr_forward<Args>(args)...) {}

    ~storage_t() = default;
};


template <class T>
struct optional_base
{
    bool init_;
    storage_t<T> storage_;

    constexpr optional_base() noexcept : init_(false), storage_(trivial_init) {};

    explicit constexpr optional_base(const T& v) : init_(true), storage_(v) {}

    explicit constexpr optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {}

    template <class... Args> explicit optional_base(in_place_t, Args&&... args)
        : init_(true), storage_(constexpr_forward<Args>(args)...) {}

    template <class U, class... Args, TR2_OPTIONAL_REQUIRES(is_constructible<T, std::initializer_list<U>>)>
    explicit optional_base(in_place_t, std::initializer_list<U> il, Args&&... args)
        : init_(true), storage_(il, std::forward<Args>(args)...) {}

    ~optional_base() { if (init_) storage_.value_.T::~T(); }
};
Run Code Online (Sandbox Code Playgroud)

笔记:

如果您想得到一个答案,既支持在constexpr函数中的局部变量中使用,又支持在运行时使用非平凡可破坏的值,则此解决方案存在一些复杂性。(可能,您确实想支持这一点,您不希望constexpr optional泄漏,或者它不是常规可选的替代品。)

这是因为constexpr根据语言规则必须默认析构函数,但在某些情况下,这必须与调用泛型参数的析构函数的需要相协调。

在 Andrzej 的示例中,这是通过使用 SFINAE 并切换std::is_trivially_destructibleoptional_base类的两种不同实现来解决的,一种具有默认析构函数,另一种没有。我在上面的清单中省略了这一点。如果你想要所有血腥的细节,我建议你阅读 Andrzej 的代码。

  • @james 好吧,欢迎在 C++ 中实现接近语言级别的功能。;) (4认同)