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

C++ 可以使用std::launder将对象指针转换为其封闭数组指针吗?,c++,language-lawyer,c++17,C++,Language Lawyer,C++17,当前的标准草案(大概是C++17)规定: [注意:数组对象及其第一个元素不是指针可相互转换的,即使它们具有相同的地址- 尾注 ] 因此,不能对指向对象的指针进行重新解释以获取其封闭数组指针 现在,有了std::launder,: 模板[[nodiscard]]constexpr T*流槽(T*p)无异常 要求:p表示内存中某个字节的地址A。在其生存期内且类型类似于T的对象X位于地址A。通过结果可以访问的所有存储字节都可以通过p访问(见下文) 可及性的定义如下: 备注:只要核心常量表达式中可以使用

当前的标准草案(大概是C++17)规定:

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

因此,不能对指向对象的指针进行重新解释以获取其封闭数组指针

现在,有了
std::launder
,:

模板[[nodiscard]]constexpr T*流槽(T*p)无异常

要求:
p
表示内存中某个字节的地址A。在其生存期内且类型类似于T的对象X位于地址A。通过结果可以访问的所有存储字节都可以通过
p
访问(见下文)

可及性的定义如下:

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

现在,乍一看,
std::launder
is似乎可以用来进行前面提到的转换,因为我强调了这一部分

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


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

备注:任何非精神分裂症编译器都可能乐意接受这一点,因为它将接受C样式转换或重新解释转换,所以试一试并不是一个选项

但是,恕我直言,你的问题的答案是否定的。如果Y是数组元素,则强调的立即封闭数组对象位于注释段落中,而不是Requires段落中。这意味着,如果Requires部分得到尊重,则注释one也适用。由于数组及其元素类型不相似,因此该要求不满足无法使用sfied和
std::launder


以下是更一般的(哲学?)解释。在K&R C时代(70年代),C旨在取代汇编语言。因此,规则是:如果源代码可以翻译,编译器必须服从程序员。因此,没有严格的别名规则,指针也不再是带有附加算术规则的地址。这在C99和C++03(更不用说C++11+)中发生了强烈的变化程序员现在应该使用C++作为高级语言。这意味着指针只是一个对象,它允许访问给定类型的另一个对象,数组和元素类型是完全不同的类型。内存地址现在只不过是实现细节。所以尝试将指针转换成数组。将r添加到它的第一个元素违反了语言的哲学,可能会在编译器的更高版本中咬到程序员。当然,现实生活中的编译器出于兼容性的原因仍然接受它,但我们甚至不应该尝试在现代程序中使用它。

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

我想一些例子可能会有所帮助。下面的每个例子都是
reinterpret\u cast
s a
int*
指向10
int
s数组的第一个元素,将其转换为
int(*)[10]
。由于它们不是指针可相互转换的,
reinterpret\u cast
不会更改指针值,因此您会得到一个
int(*)[10] 
的值为“指向(无论数组是什么)的第一个元素的指针”。然后,每个示例都尝试通过调用强制转换指针上的
std::launder来获取指向整个数组的指针

int x[10];
auto p = std::launder(reinterpret_cast<int(*)[10]>(&x[0])); 
这是未定义的。您只能通过源指针访问
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
这是(打算)未定义的。您本来可以从结果中访问
x4[1]
,因为
x4[0]。a
是指针可与
x4[0]
相互转换的,因此指向前者的指针可以
重新解释
以生成指向后者的指针,然后可用于指针算术。请参阅

结构Y{inta[10];双Y;}x5; 自动p3=std::流槽(重新解释铸件(&x5.a[0]);

这也是未定义的,因为您本来可以从结果指针(通过将
转换为
y*
)访问
x5.y
)但是不能使用源指针访问它。

数组与其第一个元素位于同一地址,并且数组类型肯定与自身相似,因此requires部分确实适用。@n.m:只是澄清一下:你的意思是,答案是“是”吗?是的,我想是的。Discl
struct X { int a[10]; } x3, x4[2]; // assume no padding
auto p3 = std::launder(reinterpret_cast<int(*)[10]>(&x3.a[0])); // OK
auto p4 = std::launder(reinterpret_cast<int(*)[10]>(&x4[0].a[0])); 
struct Y { int a[10]; double y; } x5;
auto p3 = std::launder(reinterpret_cast<int(*)[10]>(&x5.a[0]));