可以使用std :: launder将对象指针转换为其封闭的数组指针吗?

gez*_*eza 12 c++ language-lawyer c++17

目前的标准草案(可能是C++ 17)在[basic.compound/4]中说:

[注意:数组对象及其第一个元素不是指针可互换的,即使它们具有相同的地址. - 结束说明]

因此,指向对象的指针不能reinterpret_cast获得其封闭的数组指针.

现在,有std::launder,[ptr.launder/1]:

template<class T> [[nodiscard]] constexpr T* launder(T* p) noexcept;

要求:p表示内存中字节的地址A. 在其生命周期内且其类型类似于T的对象X位于地址A处.可通过结果到达的所有存储字节都可通过p(见下文)到达.

和的definion 可达是在[ptr.launder/3] :

备注:只要可以在核心常量表达式中使用其参数的值,就可以在核心常量表达式中使用此函数的调用.如果对象Y位于Y所占用的存储区内,则指向存储的字节可以到达,如果Y是指针可互换的对象,则指向对象Y,或者如果Y是数组元素,则指向立即封闭的数组对象.如果T是函数类型或cv void,则程序格式错误.

现在,乍一看,似乎std::launder可以用来做上述转换,因为我强调的部分.

但.如果p指向数组的对象,则根据此定义可以访问数组的字节(即使p不是指针可互换为数组指针),就像清洗的结果一样.因此,该定义似乎没有说明这个问题.

那么,可以std::launder用来将对象指针转换为其封闭的数组指针吗?

T.C*_*.C. 9

这取决于封闭数组对象是否是一个完整的对象,如果没有,是否可以通过指向该封闭数组对象的指针有效地访问更多字节(例如,因为它是一个数组元素本身,或者指针可以与一个更大的对象互相转换) ,或与指向对象的指针可互换的数组元素."可达"要求意味着您不能使用launder获取指针,该指针允许您在未定义行为的情况下访问比源指针值允许的更多字节.这可以确保某些未知代码可能调用的可能性launder不会影响编译器的转义分析.

我想一些例子可能有所帮助.下面各实施例reinterpret_castSA int*指向数组10的第一个元素ints转换为int(*)[10].由于它们不是指针可互换的,因此reinterpret_cast不会更改指针值,并且您获得int(*)[10]的值为"指向第一个元素的指针(无论数组是什么)".然后,每个示例都尝试通过调用转换指针来获取指向整个数组的std::launder指针.

int x[10];
auto p = std::launder(reinterpret_cast<int(*)[10]>(&x[0])); 
Run Code Online (Sandbox Code Playgroud)

还行吧; 您可以x通过源指针访问所有元素,并且结果launder不允许您访问其他任何内容.

int x2[2][10];
auto p2 = std::launder(reinterpret_cast<int(*)[10]>(&x2[0][0])); 
Run Code Online (Sandbox Code Playgroud)

这是未定义的.您只能x2[0]通过源指针访问元素,但结果(可能是指针x2[0])将允许您访问x2 [1],而您无法通过源.

struct X { int a[10]; } x3, x4[2]; // assume no padding
auto p3 = std::launder(reinterpret_cast<int(*)[10]>(&x3.a[0])); // OK
Run Code Online (Sandbox Code Playgroud)

还行吧.同样,您无法通过指向x3.a您无法访问的任何字节的指针进行访问.

auto p4 = std::launder(reinterpret_cast<int(*)[10]>(&x4[0].a[0])); 
Run Code Online (Sandbox Code Playgroud)

这是(打算)未定义的.你可以x4[1]从结果到达因为x4[0].a是指针可互换的x4[0],所以指向前者的指针可以reinterpret_cast产生一个指向后者的指针,然后可以用于指针算术.请参阅https://wg21.link/LWG2859.

struct Y { int a[10]; double y; } x5;
auto p3 = std::launder(reinterpret_cast<int(*)[10]>(&x5.a[0])); 
Run Code Online (Sandbox Code Playgroud)

这又是未定义的,因为您可以x5.y从结果指针(reinterpret_cast到a Y*)到达,但源指针不能用于访问它.