C++ std::洗槽替代方案c和x2B+;17

C++ std::洗槽替代方案c和x2B+;17,c++,c++14,language-lawyer,c++17,stdlaunder,C++,C++14,Language Lawyer,C++17,Stdlaunder,它类似于std::optional,但不存储额外的bool。用户必须确保仅在初始化后访问 template<class T> union FakeOptional { //Could be a normal struct in which case will need std::aligned storage object. FakeOptional(){} //Does not construct T template<class... Args>

它类似于std::optional,但不存储额外的bool。用户必须确保仅在初始化后访问

template<class T>
union FakeOptional { //Could be a normal struct in which case will need std::aligned storage object.
    FakeOptional(){}  //Does not construct T
    template<class... Args>
    void emplace(Args&&... args){
        new(&t) T{std::forward<Args&&>(args)...};
    }
    void reset(){
        t.~T();
    }
    operator bool() const {
        return true;
    }
    constexpr const T* operator->() const {
        return std::launder(&t);

    }
    constexpr T* operator->() {
        return std::launder(&t);
    }
    T t;
};
模板
union FAKEOPERTIONAL{//可以是一个普通结构,在这种情况下需要std::aligned存储对象。
FakeOptional(){}//不构造T
模板
空侵位(Args&…Args){
新建(&t)t{std::forward

问题:

  • 忽略
    std::launder
    可以吗?我想不行
  • 由于
    std::launder
    仅在c++17中可用,如何在c++14中实现上述类?
    boost::optional
    std::experimental::optional
    应该需要类似的功能,或者它们是否使用了特定于编译器的魔法


  • 注意:很容易错过,类型被声明为
    union
    。这意味着
    T
    的构造函数实际上没有被调用。Ref:

    不,您不能。提出
    std::launder
    的原因之一是
    std::optional
    在C++14中不可实现。有关详细信息,请参阅

    另一方面,您可以在不使用
    constexpr
    的情况下实现它。其思想是使用带有
    reinterpret\u cast
    的缓冲区,因为
    reinterpret\u cast
    的结果将始终引用新创建的对象(在C++17中仍然需要
    std::launder
    ,但在C++14中这是可以的)

    template<class T>
    struct FakeOptional { 
        FakeOptional(){}  
        template<class... Args>
        void emplace(Args&&... args){
            new(&storage) T{std::forward<Args&&>(args)...};
        }
        void reset(){
            reinterpret_cast<T*>(&storage)->~T();
        }
        operator bool() const {
            return true;
        }
        const T* operator->() const {
            return reinterpret_cast<const T*>(&storage);
        }
        T* operator->() {
            return reinterpret_cast<T*>(&storage);
        }
        std::aligned_storage_t<sizeof(T), alignof(T)> storage;
    };
    
    模板
    结构FakeOptional{
    FakeOptional(){}
    模板
    空侵位(Args&…Args){
    
    new(&storage)T{std::forward,不实现
    constexpr
    语义(您可以参考它的详细信息)。

    只需将new放入字节数组中即可+
    重新解释转换
    运算符bool()const{return true;}
    FakeOptional
    。是的,听起来不错。@FrançoisAndrieux它是一个并集,而不是结构。因此它不是构造的。这就是这种类型的全部要点。避免默认构造,但只需分配足够的空间,以后可能会用到。@Nicolas,我正在使用一个并集。注意:
    union FakeOptional
    它完全是一个构造对于编译器来说,你必须有编译器支持。你能做的最好的事情就是检查是否有
    \u内置的\u launder
    或类似的东西。我想知道,在现在的编译器中,
    std::launder
    是否是必要的。在这种情况下,如果常量成员没有随func而改变,Clang、GCC、MSVC或ICC都不会优化代码假设调用。这样我们就可以推断出
    std::launder
    是不必要的。但是我们不能从一个例子中得出一条定律。你知道编译器进行优化的一个例子吗?这将证明你的方法是必要的,以及先前归纳推理的弱点。但是指针是“普通类型”正式地说,指针的字节副本就可以了!或者普通类型并不是真正的“普通”@Oliv当然,我的方法只在语言律师的方式下才有必要。对const/ref成员的限制,因此如果启用这种优化,一些旧代码可能会被破坏。我想这就是为什么这些编译器有点保守的原因之一。@curiousguy抱歉,我不明白你的意思。你为什么提到指针复制?@curiousguy Ohh的确,我忘记了C++引用中的const引用不是一个接口契约:(但是!const对象(包括成员SubEdEx)是一个契约,因为它禁止更改const对象的值。