【问题标题】:constexpr std::optional possible implementationconstexpr std::optional 可能的实现
【发布时间】:2016-06-18 07:24:25
【问题描述】:

我正在尝试实施std::optionalconstexpr 支持作为一种实践。用法如下:

constexpr optional<int> x(123);
int arr[*x];

在尝试实现这一点时,我遇到了一个我无法解决的问题:在optional&lt;T&gt; 对象中,我使用std::aligned_storage_t&lt;sizeof (T), alignof (T)&gt; 对象来存储值,并在optional&lt;T&gt; 中使用placement new的构造函数将值构造到存储中。但是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`
}

我还能如何实现这个?

【问题讨论】:

    标签: c++ c++11 c++14 c++17


    【解决方案1】:

    你可以使用联合。

    看看 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(); }
    };
    

    注意:

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

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

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

    【讨论】:

    • 哇,这真是比我预期的要多得多。
    • @james 好,欢迎在 C++ 中实现接近语言级别的功能。 ;)
    • 你不需要析构函数是 constexpr 来让这个 DIY 选项在 constexpr 上下文中可用吗?
    • @ChrisBeck 因此我的评论。我检查了 Andrzej 的可选实现,现在我看到他有条件地从 constexpr_optional_base 继承,如果 T 可以轻易破坏,则没有析构函数。恐怕您的 sn-p 仅显示如何为运行时案例实现可选存储。
    猜你喜欢
    • 2014-11-08
    • 1970-01-01
    • 1970-01-01
    • 2013-06-05
    • 2019-02-19
    • 2021-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多