3

我已经多次看到以下模式:

// 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令人愉快的接口(你可以看到它在这里工作。

现在,std::aligned_storage很简洁,但是 C++17 为我们提供了一个用于存储与对象生命周期分割的新工具,即std::optional.

但是,访问std::optional(value()operator*) 值的两种方法都需要一个值才能实际存在。否则value()将 throw 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以某种方式仍然可以使用这种用法吗?
如果没有,阻止它的原因是什么?

4

1 回答 1

3

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

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

请注意,这种情况与该问题中描述的情况不同。在那个问题中,引用/指针是在创建类型的对象之后T获得的(尽管在没有 的C++17 之后这也可能是未定义std::launder的)。

以某种方式仍然可以使用 std::optional 吗?

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

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

优化器可能会发现该地址与为 提供存储的对象相关联T,并忽略通过导致未定义行为的类型的 glvalue 对该地址的任何访问。事实上,原因本质上是严格别名规则如何使优化器受益

于 2017-12-26T16:04:19.100 回答