通过强制转换创建子数组引用

Creating sub-array reference via cast?

本文关键字:数组 引用 创建 转换      更新时间:2023-10-16

我想调用一个签名如下的函数:

void foo(int (&ra)[2]);

也就是说,它的自变量是对两个元素的数组的引用。让我们假设该函数的作者(将保持匿名)不倾向于更改接口。

我想调用现有数组的子数组上的函数。像这样:

void bar()
{
int vals[] = { 1, 2, 3, 4, 5 };
foo(reinterpret_cast<int (&)[2]>(vals[1]));
}

GCC接受这段代码,并将其编译成我想要的代码,但我根本不确定这是否能得到语言的保证。

此代码实现的行为是否已定义(或未定义)?如果是这样的话,有没有一种方法可以重写bar(),以一种定义良好的方式做同样的事情?

若你们引用C++03或C++11标准的章节,我更有可能接受你们的答案。

不清楚。C++11[dcl.ref]/5表示

[…]引用应初始化为引用有效对象或功能。

但是什么是";有效的";对象当然,在该存储器位置处不存在类型为int[2]的对象。但话说回来,人们普遍认为,如果构造本身具有定义良好的行为,则允许引用绑定到分配的内存,稍后将在其中构造适当类型的对象。(事实上,尽管C++11没有这样做,但标准库规范的后续版本使用了一个名为voidify的仅用于说明的函数来定义std::uninitialized_copy,该函数将在调用placement new之前将引用绑定到未初始化的内存位置。因此,基本上,库规范依赖于这样一个事实,即在至少某些情况下,甚至在某些情况下允许绑定引用当没有活动对象时。)

这是CWG 453的主题,这个问题太老了,CWG已经很久没有讨论过了,所以任何人都不知道它最终会如何解决(如果有的话),除了最终的解决方案可能会将DR恢复到C++98(这可以被解释为追溯性地修复旧的标准)。

一旦foo的参数被初始化,另一个问题就出现了,允许对它做什么。例如,这有效吗?

int x = ra[1];

评估这个表达式的第一步是数组到指针的转换,在[conv.array]:中进行了描述

类型为"的左值或右值;CCD_ 5的阵列CCD_;或";CCD_ 7"的未知界数组;可以转换为类型为"0"的prvalue;指向T的指针";。结果是指向数组的第一个元素的指针。

但是ra没有引用大小为2的实际数组,所以该假设数组的第一个元素也是假设的。因此,这似乎会给UB。但话说回来,谁知道呢?

让我们看看C++20,记住C++17改变了指针的规则,使指针指向对象,而不是地址。(尽管它们"代表"地址。)reinterpret_cast由[expr.relprete.cast]/11:控制

T1类型的glvalue表达式,指定对象x,可以强制转换为类型";参考CCD_ 12";如果类型为"0"的表达式;指向CCD_ 13的指针";可以显式地转换为类型";指向CCD_ 14的指针";使用CCD_ 15。结果是CCD_ 16的结果,其中CCD_;指向CCD_ 18的指针";。[…]

因此,首先取一个指向vals[1]的指针,然后将该指针取为指向int (*)[2]reinterpret_cast。在[expr.relprete.cast]/7下,首先执行static_castvoid*,然后执行第二个static_castint (*)[2]。第一个static_cast只是进行隐式转换,在[conv.ptr]/2下,对cvvoid*的隐式转换保持值不变:也就是说,结果仍然指向vals[1]处的int对象。第二个static_cast是来自void*的静态强制转换,由[expr.static.cast]/13管理,根据该规则

类型为";指向cv1CCD_ 32的指针";可以转换为类型为"0"的prvalue;指向cv2CCD_ 33"的指针;,其中T为对象类型和cv2是与cv1相同或更大的cv资格。如果原件指针值表示存储器中字节的地址A,并且A不满足T的对齐要求,则所得到的指针值未指定。否则,如果原始指针值指向对象a,并且存在T类型的对象b(忽略cv限定),该对象与a是指针可交换的(6.8.3),结果是指向b的指针。否则,指针值将因转换而保持不变。

void*表示(而不是指向)的内存位置上没有类型为T(即int[2])的对象,因此其值(指向的内容)保持不变;第二个static_cast的结果仍然是指向vals[1]处的int对象的指针,但它具有类型int (*)[2]。这可能看起来有点奇怪,但事实就是这样。作为评估reinterpret_cast的最后一步,这个指针被取消引用。在[expr.unary.op]/1下,这会给出一个左值,它指的是指针操作数指向的任何对象。因此,reinterpret_cast的最终结果是int[2]类型的左值,该左值指的是单个int对象(N0)。

使用该左值初始化ra是否有效?大概也许不是。这将导致ra被初始化为引用一个活动的对象。。。但它没有适合CCD_ 53的类型。可能应该有一个规则,使UB。但事实并非如此。

如果达到数组到指针的转换,会发生什么?[conv.array]/1表示:

类型为"的左值或右值;CCD_ 54CCD_;或";CCD_ 56"的未知结合的阵列;可以转换为类型为"0"的prvalue;指向T的指针";。应用临时物化转换(7.3.5)。结果是指向数组的第一个元素。

什么数组?ra甚至不引用数组;它指的是CCD_ 60处的CCD_。再说一遍,我猜这是UB?但这个故事的寓意是,在这方面的标准没有得到明确规定。