std::launder 替代 pre c++17

Posted

技术标签:

【中文标题】std::launder 替代 pre c++17【英文标题】:std::launder alternative pre c++17 【发布时间】:2019-06-13 01:37:22 【问题描述】:

类似于std::optional,但不存储额外的布尔值。用户必须确保只有在初始化后才能访问。

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) Tstd::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;
;

如果您想知道为什么我需要这样一个晦涩难懂的数据结构,请在此处查看:https://gitlab.com/balki/linkedlist/tree/master

问题

    可以忽略std::launder吗?我猜不是。 既然std::launder只在c++17中可用,那么如何在c++14中实现上面的类呢? boost::optionalstd::experimental::optional 应该需要类似的功能,还是他们使用了特定于编译器的魔法?

注意:很容易漏掉,类型声明为union。这意味着 T 的构造函数实际上没有被调用。参考:https://gcc.godbolt.org/z/EVpfSN

【问题讨论】:

只需将 new 放入字节数组 + reinterpret_cast 即可。 operator bool() const return true; ...FakeOptional。是的,听起来不错。 @FrançoisAndrieux 这是一个联合,而不是结构。所以它没有被构造。这就是这种类型的全部要点。避免默认构造,但只分配足够的,以后可以使用。 @NicolBolas,我正在使用联合。注意:union FakeOptional 它完全是编译器的构造,你必须有编译器支持。你能做的最好的就是检查是否有__builtin_launder 或类似的东西。 【参考方案1】:

不,你不能。提出std::launder 的原因之一是std::optional 在C++14 中无法实现。详情可以参考this discussion。

另一方面,您可以在没有constexpr 的情况下实现一个。这个想法是使用带有reinterpret_cast 的缓冲区,因为reinterpret_cast 的结果将始终引用新创建的对象(在C++17 中std::launder 仍然需要,但在C++14 中这很好)。例如,

template<class T>
struct FakeOptional  
    FakeOptional()  
    template<class... Args>
    void emplace(Args&&... args)
        new(&storage) Tstd::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;
;

实现boost::optionaluses this idea并没有实现constexpr语义(具体可以参考它的source code)。

【讨论】:

我想知道,对于现在的编译器,std::launder 是否是必要的。在这个compiler explorer page 中,Clang、GCC、MSVC 或 ICC 都没有优化代码,假设 const 成员在函数调用中没有改变。这样人们就可以推断std::launder 是不必要的。但是不能从一个例子中制定法律。您知道编译器进行优化的示例吗?这将证明你的方法是必要的,以及之前归纳推理的弱点。 But but but 指针是正式的“普通类型”,所以指针的字节副本应该可以!或者琐碎的类型并不是真正的“琐碎”。 @Oliv 当然,我的方法只需要以语言律师的方式。 const/ref 成员 is added in C++03 的限制,因此如果启用此类优化,一些旧代码可能会中断。我猜这就是这些编译器有点保守的原因之一。 @curiousguy 对不起,我不明白你的意思。为什么提到指针复制? @curiousguy 哦,是的,我忘记了 const 引用不是 C++ 中的接口契约 :((!但是 const 对象(包括成员 suboject)是契约,因为禁止更改一个常量对象。

以上是关于std::launder 替代 pre c++17的主要内容,如果未能解决你的问题,请参考以下文章

为啥这里需要 std::launder ?

为啥要引入 `std::launder` 而不是让编译器处理它?

我在哪里可以找到 std::launder 的真正作用? [复制]

如何解释 std::launder 的可达性要求?

带有就地多态容器的 std::launder

std::launder 的效果是不是在调用它的表达式之后持续?