std::launder的替代方案(适用于C++17之前)

14

这就像是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;
};

如果您想知道为什么我需要这样一个不常用的数据结构,请查看这里:https://gitlab.com/balki/linkedlist/tree/master 问题:
  1. 可以忽略 std::launder 吗?我认为不行。
  2. 由于 std::launder 仅在c++17中可用,如何在c++14中实现上述类?boost::optionalstd::experimental::optional 是否需要类似的功能,或者它们使用了编译器特定的技术?

注意:很容易忽略,该类型被声明为 union。这意味着真正没有调用 T 的构造函数。参考链接:https://gcc.godbolt.org/z/EVpfSN


4
operator bool() const { return true; }... FakeOptional。没错,听起来很对。 - Barry
2
@FrançoisAndrieux 这是一个联合体,而不是结构体。因此它不会被构造。这就是这种类型的全部意义所在。避免默认构造,只需分配足够的空间,以便稍后使用。 - balki
1
@NicolBolas,我正在使用一个联合体。注意:union FakeOptional - balki
3
这完全是编译器的构造,你必须有编译器支持。你能做的最好的就是检查是否存在 __builtin_launder 或者类似的东西。 - Passer By
@VTT,你的意思是说,我只需要这样做,而不用担心launder吗? constexpr T* operator->() {return &t;} - balki
显示剩余5条评论
1个回答

4
不行,其中一个提出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) 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;
};
< p>实现boost::optional 使用了这个思想,但没有实现constexpr语义(您可以参考其源代码进行详细了解)。


1
我想知道现在的编译器是否需要使用std::launder。在这个编译器浏览器页面中,无论是Clang、GCC、MSVC还是ICC都没有优化代码,假设const成员在函数调用期间不会被更改。因此可以推断出std::launder是不必要的。但是不能从一个例子中得出结论。你知道有哪些例子可以证明编译器进行了优化吗?这将证明你的方法是必要的,并且之前归纳推理的弱点。 - Oliv
但是指针在官方上被定义为“平凡类型”,因此指针的字节复制应该可以做到!或者平凡类型并不真正“平凡”。 - curiousguy
2
@Oliv 我的方法只是以语言律师的方式必要的。当然,const/ref成员的限制在C++03中添加,因此如果启用了这种优化,一些旧代码可能会出现问题。这也是这些编译器有点保守的原因之一,我想。 - xskxzr
@xskxzr 除了复制官方的平凡标量类型,std::launder 还有什么作用? - curiousguy
1
@curiousguy 噢,没错,我忘记了在C++中const引用不是接口合约 :((!但是const对象(包括成员子对象)是一个合约,因为禁止更改const对象的值。 - Oliv
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接