感谢大家的回复和评论,但我认为正确的答案是 - 就目前而言,代码展示了技术 UB,尽管可以纠正。我已经查看了其中一些问题 [1, 2] @xskxzr 已链接,它引导我找到this quote from the standard:
如果两个对象是指针可互转换的,那么它们具有相同的
地址,并且可以从指针中获得指向一个的指针
通过 reinterpret_cast 到另一个。 [ 注意:一个数组对象及其
第一个元素不是指针可相互转换的,即使它们有
相同的地址。 — 尾注 ]
然后在reinterpret_cast page 上有如下注释和一个例子:
假设满足对齐要求,reinterpret_cast 会
在少数有限的情况下不改变指针的值
处理 pointer-interconvertible 对象:
int arr[2];
int* p5 = reinterpret_cast<int*>(&arr); // value of p5 is unchanged by reinterpret_cast and
// is "pointer to arr"
即使这个compiles without warning and runs,这在技术上是一个UB,因为p5在技术上仍然是一个指向arr而不是arr[0]的指针。所以基本上我使用reinterpret_cast的方式导致了UB。考虑到上述情况,如果我要直接创建int * 到第一个int(根据@codekaizer 的回答可以),那么这应该是有效的,对吧?:
template<typename T, size_t X, size_t Y>
void sort2(T(&arr)[X][Y])
{
T *p = &arr[0][0]; // or T *p = arr[0];
std::sort(p, p + X * Y);
}
但它也可能是 UB,因为指针 p 指向具有 Y 元素的 Ts 的第一个数组的第一个 T。因此p + X*Y 将指向Ts 的第一个数组的范围之外,因此是UB(再次感谢@xskxzr 的link 和评论)。
如果表达式 P 指向具有 n 的数组对象 x 的元素 x[i]
元素,表达式 P + J 和 J + P(其中 J 的值为 j)
如果 0≤i+j≤n,则指向(可能是假设的)元素 x[i+j];
否则,行为未定义。
所以这是我放弃前的最后一次尝试:
template<typename T, size_t X, size_t Y>
void sort2(T(&arr)[X][Y])
{
T(&a)[X * Y] = reinterpret_cast<T(&)[X * Y]>(arr);
std::sort(a, a + X * Y);
}
这里T arr[X][Y] 首先转换为T a[X*Y],再次使用reinterpret_cast,我认为它现在是有效的。重新解释的数组a 愉快地衰减为指向数组a[X*Y] 的第一个元素的指针(a + X * Y 也在范围内)并转换为std::sort 中的迭代器。
TL;DR 版本
由于reinterpret_cast 使用不当,OP 中的行为未定义。将二维数组转换为一维数组的正确方法是:
//-- T arr2d[X][Y]
T(&arr1d)[X*Y] = reinterpret_cast<T(&)[X*Y]>(arr2d);
T1 类型的左值表达式可以转换为引用
另一种T2。结果是一个左值或 xvalue,指的是
与原始左值相同的对象,但类型不同。不
临时创建,没有复制,没有构造函数或转换
函数被调用。结果引用只能访问
如果类型别名规则允许,则安全
Aliasing rules:
每当尝试读取或修改存储的值时
DynamicType 类型的对象通过 AliasedType 类型的左值,
除非满足以下条件之一,否则行为未定义:
- AliasedType 和 DynamicType 类似。
Type similarity:
非正式地,两个类型是相似的 if,忽略顶层
简历资格
- 它们都是相同大小的数组或都是未知边界的数组,并且数组元素类型相似。
Array element type:
在T D 的声明中,D 的格式为
D1 [ constant-expression opt ] attribute-specifier-seq opt
声明TD1中标识符的类型是
“derived-declarator-type-listT”,则D的标识符的类型为数组类型;如果D 标识符的类型包含自动type-specifier,则程序格式错误。 T 被称为数组元素类型;