引用一个空的可选值
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 &t = reinterpret_cast<T &>(storage);
表现出未定义的行为 - 或者更确切地说,t
的后续使用会。正确的做法是T& t = *new(&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 恐怕是这样。以上是关于引用一个空的可选值的主要内容,如果未能解决你的问题,请参考以下文章