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

时间:2018-01-10 13:55:41

标签: c++ pointers language-lawyer c++17 unions

此问题遵循此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是,则指向立即封闭的数组对象数组元素。

1 个答案:

答案 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::launderstd::launder中间的某个位置是必需的。