考虑以下代码:
#include <iostream>
#include <string>
union U
{
std::string s;
U()
{
new(&s) std::string;
}
void Set(std::string new_s)
{
s.~basic_string();
new(&s) std::string(std::move(new_s));
}
~U()
{
s.~basic_string();
}
};
int main()
{
U u;
u.Set("foo");
std::cout << u.s << '\n'; // Do I need `std::launder` here?
}
我知道如果我使用字节数组而不是联合,我必须std::launder(&s)
访问字符串。但我在这里需要它吗?
常识性的答案似乎是“不”,但是标准在哪里可以保证union
不需要呢std::launder
?
成员访问表达式
u.s
的定义很简单,就是生成一个指向联合成员子对象的左值。(即使不在其生命周期内,即不活跃,也是s
如此。)参见[expr.ref]/6.2。u.s
所以没有必要清洗。您已经拥有了所需对象的泛左值。
即使你从指向联合对象的指针开始,并尝试使用指针转换方法(更类似于字节数组方法),它仍然可以工作
std::launder
,例如:这是因为根据[basic.compound]/4.2 ,联合对象可以与其任何成员子对象进行指针互换,这意味着根据[expr.static.cast]/14 ,将立即生成指向成员子对象的指针。
reinterpret_cast
std::launder
如果由于源对象和目标对象之间缺乏指针相互转换,转换结果仍然指向原始对象,则需要这样做。而且,根据[basic.object]/2和[basic.life]/8,您创建的对象
new
将成为联合的成员子对象,并将s
指定u
最新的这样的子对象,因为它们满足这些段落中提到的所有要求(特别是与的完全重叠s
和与的完全类型匹配s
)。因此
s.u
也不会意外地引用std::string
已经被后续子对象替换的前一个子对象new
。