数组转指针转换的措辞歧义与未定义行为技术问询
根据C++标准[conv.array]章节,数组转指针转换定义如下(加粗为提问者标注):
类型为“N个T的数组”或“未知边界的T数组”的左值或右值可转换为“指向T的指针”纯右值,会应用临时物化转换([conv.rval]),结果为指向该数组首元素的指针。
该措辞默认转换表达式指向标注的“该数组”,但存在合法场景:类型为T[N]的左值实际指向T类型对象,示例代码如下:
int a[5]; auto pa = reinterpret_cast<int(*)[5]>(&a[0]);
此时*pa是int[5]类型左值,但实际指向单个int对象(a的首元素)——因为int[5]与int并非指针可互转换类型,reinterpret_cast未改变指针值。而*std::launder(pa)明确指向int[5]对象(即a),对其应用数组转指针转换的行为清晰。但对*pa应用该转换时,措辞中的“该数组”含义模糊,有观点认为此行为属于“因遗漏导致的未定义行为”,但标准段落未限定“表达式需指向数组”,导致行为界定存疑。
1. 按当前标准措辞,此行为是否确实为未定义行为?若是,原因是什么?
是未定义行为。核心原因在于:*pa的静态类型是int[5],但它并未指向一个实际存在的int[5]数组对象,而是指向单个int对象。C++标准要求,访问对象的表达式其静态类型需与所指对象的动态类型(或多态场景下的基类)匹配,或符合reinterpret_cast的特定规则。数组转指针转换本质是对数组首元素的访问,当表达式未指向真实数组对象时,标准未定义该转换的行为,因此属于未定义行为。
此外,[basic.lval]章节明确规定,左值表达式静态类型与所指对象动态类型不匹配时,除char*访问任意对象等少数例外,访问行为本身就是未定义的,数组转指针转换自然遵循这一规则。
2. 是否存在非显式的定义方式?若是,具体是怎样的?
不存在非显式的合法定义方式。要让该转换行为符合标准定义,必须确保表达式指向真实的数组对象,唯一合法途径是通过std::launder修正指针的类型关联:*std::launder(pa)会明确绑定到数组a,此时对其应用数组转指针转换完全符合[conv.array]的规则,结果为指向a首元素的指针。
任何绕过std::launder直接使用*pa进行转换的操作,都会因静态类型与动态类型不匹配而落入未定义行为范畴。
3. 标准措辞是否存在不严谨之处?
是的,[conv.array]的措辞存在不严谨性。该章节描述默认转换表达式指向真实数组对象,但未明确说明当表达式静态类型为数组、但实际未指向数组对象时的行为。这种遗漏易导致开发者误解,认为只要表达式静态类型是数组,转换就合法,忽略了“表达式必须指向真实数组对象”的隐含前提。
理想的措辞应补充限定:“当该左值或右值指向一个实际存在的数组对象时,转换结果为指向该数组首元素的指针”,以此明确排除静态类型匹配但无对应对象的场景,消除歧义。
内容的提问来源于stack exchange,提问作者YurkoFlisk

