此问题遵循此one
让我们考虑一下这个示例代码:
struct sso
{
union{
struct {
char* ptr;
char size_r[8];
} large_str;
char short_str[16];
};
bool is_short_str() const{
return *std::launder(short_str+15)=='\0'; //UB?
}
};
如果short_str
不是取消引用不带std::launder
指针的活动成员,则为UB。让我们考虑ABI已经明确指定,并且我们知道size_r [7]与short_str [15]处于同一地址。当std::launder(short_str+15)
不是联盟的活跃成员时,size_r[7]
会返回指向short_str
的指针吗?
Nota:我认为情况就是这样,因为[ptr.launder]/3
如果对象Y位于Y所占用的存储空间内,则可以通过指向对象Y的指针值来访问存储字节,Y是指针可互换的对象,如果Y是,则指向立即封闭的数组对象数组元素。
答案 0 :(得分:3)
让我们考虑一下ABI的详细规定,我们知道size_r [7]与short_str [15]位于同一地址
这完全取决于保证的确切含义。
编译器可以自由保证
Sso.short_str[15]
可以访问和修改,即使Sso.large_str
当前处于活动状态,也可以进行所有操作,并完全获得您期望的语义。
或者不给予保证免费。
对行为或程序的格式不正确或表现出未定义的行为没有任何限制。
因为那里没有对象,所以&Sso.short_str[15]
不能与任何对象进行指针互换。一个不存在的对象没有另一个对象的“相同地址”。
流水是根据指向预先存在的对象的指针定义的。然后销毁该指针,并创建一个具有相同地址的新对象(定义明确)。 std::launder
然后允许您将指针指向不再存在的对象,并获取指向现有对象的指针。
您不是在做什么。如果您使用&short_str[15]
时 处于活动状态,则将有一个指向对象的指针。而且ABI可以说这是与size_r[7]
相同的地址。现在std::launder
将在有效范围之内。
但是编译器可以走得更远,并定义short_str[15]
指向与size_r[7]
相同的对象,即使它不处于活动状态。
最弱的ABI保证,只有当您激活short_str[15]
的地址时,我才能看到与您的内容一致;稍后,您将使用large_str
,然后可以从&short_str[15]
到&size_r[7]
洗钱。与您的陈述相符的最强ABI保证使您无需致电std::launder
。 std::launder
中间的某个位置是必需的。