引用一个空的可选值

Posted

技术标签:

【中文标题】引用一个空的可选值【英文标题】:Reference to the value of an empty optional 【发布时间】:2018-06-07 05:27:09 【问题描述】:

我见过下面的模式several times:

// T is a type, this is at namespace scope
std::aligned_storage_t<sizeof(T), alignof(T)> storage;
T &t = reinterpret_cast<T &>(storage);

这与适当的命名空间和命名相结合,为变量的用户提供了一个令人愉快的界面 (t),同时通过放置 new 和显式位置在库端实现实际对象的延迟构造、重新初始化等析构函数调用。你可以看到它在工作here。

现在,std::aligned_storage 很简洁,但是 C++17 为我们提供了一个新的工具来进行这种存储与对象生命周期分割,那就是 std::optional

但是,访问std::optional 的值的两种方式(value()operator*)都需要一个实际存在的值;否则value() 将抛出std::bad_optional_access,而operator* 将触发未定义的行为(每个打破[optional.observe]§5 中的requires 子句)。

std::optional<T> storage;
T &t = *storage; // Looks okay, mines bitcoin when you're not looking

std::optional 这样的用法还有可能吗? 如果没有,阻止它的原因是什么?

【问题讨论】:

在第一个示例中不安全地使用t 需要事先运行一堆与簿记相关的检查吗? @StoryTeller 它确实需要一点谨慎,但没有什么是 SBRM 无法处理的。通常,对象实际上的作用域是main,它只是为了避免在静态初始化/销毁期间做太多事情,同时保持全局访问点。 我很确定 T &amp;t = reinterpret_cast&lt;T &amp;&gt;(storage); 表现出未定义的行为 - 或者更确切地说,t 的后续使用会。正确的做法是T&amp; t = *new(&amp;storage) T;。这基本上等同于在std::optional 中设置值。所以最后,它是一个六个,另一个六个。 @IgorTandetnik 这应该等效于指针杂耍there,这显然很好。 来自那个问题:“我使用placement new来创建对象” 【参考方案1】:

这与适当的命名空间和命名相结合,为变量的用户提供了一个令人愉快的接口 (t),同时在库端实现了实际对象的延迟构造、重新初始化等。

不幸的是,使用t 访问稍后在该地址构造的对象是未定义的行为。这是reasons 提出std::launder 的原因之一。

请注意,这种情况与that question 中描述的情况不同。在那个问题中,引用/指针是在创建T 类型的对象之后获得的(尽管this may also be undefined 在C++17 之后没有std::launder)。

std::optional 的这种用法还有可能吗?

正如您所指出的,这是未定义的行为。

如果没有,阻止它的原因是什么?

优化器可能会发现地址与为T 提供存储的对象相关联,并忽略通过导致未定义行为的类型的泛左值对该地址的任何访问。其实原因本质上是how strict-aliasing rules benefit an optimizer。

【讨论】:

嗯,这很不幸。我想std::launder 不会有帮助,因为它应该在每个使用点添加? @Quentin 恐怕是这样。

以上是关于引用一个空的可选值的主要内容,如果未能解决你的问题,请参考以下文章

Swift 中的可选值是啥?

可选类中的可选属性 VS 可选字典中的可选值

对 swift 中的可选值感到困惑

swift 通过枚举匹配的可选值(模式)

Swift 中的可选类型错误:致命错误:在展开可选值时意外发现 nil

OpenCV类VideoCapture构造函数中参数apiPreference的可选值及意义