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

Using std::launder to get a pointer to an active object member from a pointer to an inactive object?

这个问题跟在这个

让我们考虑这个示例代码:

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

如果 short_str 不是活动成员,则在没有 std::launder 的情况下取消引用指针将是 UB。让我们考虑一下 ABI 已明确指定,并且我们知道 size_r[7] 与 short_str[15] 位于同一地址。 std::launder(short_str+15) return 当 short_str 不是联盟的活跃成员时是否指向 size_r[7] 的指针?


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

A byte of storage is reachable through a pointer value that points to an object Y if it is within the storage occupied by Y, an object that is pointer-interconvertible with Y, or the immediately-enclosing array object if Y is an array element.

Let's consider that the ABI is well specified and that we know that size_r[7] is at the same address as short_str[15]

这完全取决于保证的确切含义。

编译器可以自由保证

Sso.short_str[15]
即使 Sso.large_str 当前处于活动状态,也可以访问和修改

以及所有内容,并准确获得您期望的语义。

或者不提供该保证是自由的。

对于格式错误或表现出未定义行为的行为或程序没有限制。

由于那里没有对象,&Sso.short_str[15] 不能与任何对象进行指针互换。不存在的对象没有 "same address" 作为另一个对象。

洗钱是根据指向预先存在的对象的指针定义的。然后销毁该指针,并创建一个具有相同地址的新对象(定义明确)。 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 是必需的。