感谢您的回复和评论,但我认为正确答案是 - 目前代码表现出技术 UB,虽然可以纠正。我查看了 @xskxzr 提供的一些问题 [
1,
2],并引用了标准中的
this quote from the standard:
如果两个对象可以相互转换指针,则它们具有相同的地址,并且可以通过 reinterpret_cast 从一个指针获得另一个指针。[注:即使数组对象和其第一个元素具有相同的地址,它们也不能相互转换指针。—end note]
然后在reinterpret_cast
页面上,有以下带有示例的备注:
假设满足对齐要求,reinterpret_cast 在处理涉及指针可互换对象的少数局限情况外,不会改变指针的值:
int arr[2];
int* p5 = reinterpret_cast<int*>(&arr); // value of p5 is unchanged by reinterpret_cast and
// is "pointer to arr"
尽管这
编译时没有警告并运行, 但从技术上讲,这是未定义的行为,因为
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];
std::sort(p, p + X * Y);
}
但是由于指针p指向第一个包含Y个元素的T数组的第一个T,因此它可能也是UB。 因此,p + X * Y将超出这个T数组的第一个数组范围,因此UB(再次感谢@xskxzr提供的链接和评论)。
如果表达式P指向具有n个元素的数组对象x的元素x [i],则具有值j的表达式P + J和J + P分别指向(可能是假想的)元素x [i+j],如果0≤i+j≤n; 否则,行为未定义。
因此,在我放弃之前,这是我的最后一次尝试:
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]
首先会通过
reinterpret_cast
转换为
T a[X*Y]
,我认为现在是有效的。重新解释的数组
a
可以愉快地衰减为指向数组
a[X*Y]
第一个元素的指针(
a + X * Y
也在范围内),并在
std::sort
中被转换为迭代器。
简短版本
由于不正确使用
reinterpret_cast
,OP的行为是未定义的。将二维数组转换为一维数组的正确方法是:
//-- T arr2d[X][Y]
T(&arr1d)[X*Y] = reinterpret_cast<T(&)[X*Y]>(arr2d)
类型为T1的lvalue表达式可以转换为另一种类型T2的引用。结果是一个lvalue或xvalue,引用与原始lvalue相同的对象,但具有不同的类型。不创建临时副本,不调用构造函数或转换函数。只有在类型别名规则允许的情况下,才能安全地访问所得到的引用。
类型别名规则:
每当尝试通过类型为AliasedType的glvalue读取或修改DynamicType类型的对象的存储值时,除非以下条件之一成立,否则行为未定义:
- AliasedType和DynamicType是相似的。
类型相似性:
非正式地说,如果忽略顶层cv限定符,两种类型是相似的,它们都是相同大小的数组或未知边界的数组,并且数组元素类型是相似的。
数组元素类型:
在声明 T
D
中,其中 D
的形式为
D1 [ 常量表达式 opt ] 属性说明符序列 opt
并且声明中标识符的类型为“派生声明符类型列表 T
”,则声明 D
的标识符类型是数组类型;如果声明 D
的标识符类型包含 auto 类型说明符,则程序是非法的。 T
被称为数组的 元素类型;
int *p = a[0];
怎么样? - chux - Reinstate Monica