使用 std::launder 从指向非活动对象的指针获取指向活动对象成员的指针?



这个问题紧随其后

让我们考虑此示例代码:

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)==''; //UB?
}
};

如果short_str不是活动成员,则取消引用指针而不std::launder将是 UB。让我们考虑 ABI 是明确指定的,并且我们知道 size_r[7] 与 short_str[15] 位于同一地址。当short_str不是工会的活动成员时,std::launder(short_str+15)是否返回指向size_r[7]的指针?


诺塔:我认为是这样,因为[ptr.launder]/3

可以通过

指向对象 Y 的指针值(如果对象 Y 在 Y 占用的存储内)、指针可与 Y 相互转换的对象或紧紧封闭的数组对象(如果 Y 是数组元素)来访问存储字节。

让我们考虑 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的某个地方。

最新更新