给定两个不同类型的对象及其在内存中的相对位置,我可以从指向另一个对象的指针派生出指向另一个对象的指针吗?

Mic*_*las 8 c++ pointers language-lawyer c++20 c++23

在下面的示例中,我们有两个不同类型的对象,但恰好在内存中彼此相邻。

struct X {
    alignas(long) int val = 1;
};

struct Y {
    long val = 2;
};

[...]

std::byte* buf = new std::byte[sizeof(X) + sizeof(Y)];
X* x = new (buf) X();
new (buf + sizeof(X)) Y();
Run Code Online (Sandbox Code Playgroud)

给定x,以及我们对 的实例Y实际位于内存中相对于 的位置的了解,是否有任何方法可以从符合 C++20 或 C++23 的规范x派生指向 的实例的指针?Yx

换句话说,以下代码是否符合标准,或者是否有任何方法使其符合标准:

Y* y = std::launder(reinterpret_cast<Y*>(reinterpret_cast<std::byte*>(x) + sizeof(X)));
y->val = 3;
Run Code Online (Sandbox Code Playgroud)

我怀疑该代码不符合 C++20 或 C++23 标准,原因如下:

(1) P1839R5表明实际上不可能首先获得指向对象表示形式的可用指针。

(2) 即使考虑到 P1839R5 中修订的指针算术规则,您似乎仍然无法从指向一个对象表示形式的指针转到另一种对象表示形式。

use*_*522 5

不,没有办法做到这一点。(除了可能借助合适聚合类型的隐式对象创建的帮助,请参阅我的问题

std::launder具体来说,有一个前提条件,即它不会使任何无法通过数组reinterpret_cast内的指针算术从原始指针访问到的字节变得可访问。

reinterpret_cast可以在没有 的情况下到达可相互进行指针相互转换的对象std::launder,但特别是该std::byte对象以及与std::byte该对象位于同一地址的数组对象都不能X与该对象进行指针相互转换X。(参见[basic.compound]/4。)因此,如果没有它,就无法获得指向底层数组(元素)的指针,std::launder并且std::launder由于其先决条件,使用也是不可能的。

具体来说, in 中的加法reinterpret_cast<std::byte*>(x) + sizeof(X)具有未定义的行为,因为reinterpret_cast<std::byte*>(x)指向的是X对象,std::byte而不是对象,因此违反了[expr.add]/6 。std::launder由于其先决条件,添加是不可能的。

链接的论文尝试正确指定对对象表示的访问,实际上并没有以实际使用的方式指定(例如通过转换为unsigned char*),而只是通过memcpy/std::bit_cast到合适的缓冲区。如果指定了它,我希望它的方式不应该能够解决 上放置的可达性前提条件std::launder,这似乎使得无法故意YX示例中的对象到达该对象。应该可以X从指向对象的指针仅访问对象本身的对象表示,而不是底层数组或Y对象的对象表示。