取消引用返回的引用

时间:2012-12-20 23:09:48

标签: c++ reference

假设:

int& foo(); // don't care what the reference is to
int intVal;

在以下两种情况下,右侧是相同的函数调用

int& intRef = foo();
intVal = foo();  // a reference is returned... a value is assigned.

在第二种情况下,如何将返回的引用“转换”为值?

它是由int的赋值运算符完成的吗?

3 个答案:

答案 0 :(得分:9)

在语言层面,没有“取消引用参考”这样的概念。引用实现了 lvalue 的概念。变量和引用基本相同。变量和引用之间的唯一区别是变量由编译器自动绑定到存储中的位置,而引用通常在运行时通过用户操作绑定。

在您的示例中,intRefintVal之间没有概念上的区别。两者都是int类型的左值。在概念层面,两者都通过相同的机制进行访问。您甚至可以将程序中的所有变量视为引用,这些变量由编译器隐式预先绑定。这基本上是Bjarne Stroustrup在TC ++ PL中的意思,当他说(不是逐字)时,人们可以将引用视为现有变量的替代名称。

当两者之间的差异可察觉的唯一时刻是您创建这些实体并初始化它们。引用的初始化是将其绑定到存储中的某个位置的行为。变量的初始化是将初始值复制到现有存储中的行为。

但是,一旦引用被初始化,它就会充当普通变量:读取/写入引用的行为是读取/写入它所绑定的存储位置的行为。获取引用的地址将评估它所绑定的存储位置的地址。等等。

在许多情况下,引用在内部实现为伪装的指针,即作为每次访问时隐式取消引用的不可见指针,这不是秘密。在这种情况下(当它通过指针实际实现时),每次访问时都会再次取消引用。因此,正如您在问题中提出的那样,它不是赋值运算符。事实上,您在代码中提到了该引用的名称,导致不可见指针被取消引用。

然而,实现“现有变量的替代名称”的实体不一定需要为自己存储,即在编译语言中,不需要用任何材料表示,如隐藏指针。这就是为什么语言标准在8.3.2中指出“未指明引用是否需要存储”。

答案 1 :(得分:3)

foo正在返回对#34; int"类型的对象的引用。我们不关心那个" int"来自,我们只是假设它存在。

第一行int& intRef = foo()创建intRef,它也引用完全相同的对象类型" int"由foo的返回值引用。

第二行,intVal的值被返回引用引用的对象的值替换。


回应你的意见:

你似乎在指针和引用之间变得非常困惑。引用就像对象的别名一样。对引用执行任何操作实际上会影响它引用的对象。

没有取消引用引用的东西。你只能取消引用指针。解除引用是使用一元*运算符来获取点指向的对象的行为。例如,如果您有int* p,则可以*p获取它指向的对象。这是解除引用 p

唯一可以对引用*执行的操作是它引用的对象是指针(或者它是否超载operator*)。在您的情况下,由于foo返回int&,我们无法取消引用它。表达式*foo()刚刚不能编译。这是因为foo的返回值有类型" int"这不是指针,也不会超载operator*

对于所有意图和目的,您可以将从foo返回的引用视为它所引用的对象。将此值分配给intVal与将以下代码中的x分配给intVal完全没有区别:

int intVal;
int x = 5;
intVal = x;

我确定您理解,intVal的值为x。这仅由标准定义:

  

在简单赋值(=)中,表达式的值将替换左操作数引用的对象的值。

根本不需要进行转换,因为运算符的两边都是相同的类型。

这与你的情况没什么不同。你刚才:

intVal = some_ref_to_int;

some_ref_to_int是表达式foo()。它作为参考的事实并不重要。 intVal接收引用所表示的对象的值。

答案 2 :(得分:1)

分配给intVal是标准中5.17 [exp.ass]中定义的赋值表达式赋值表达式的语法规则非常复杂,取决于其他几个语法规则,但基本上你需要在=运算符的左侧有一个可修改的左值,以及一个prvalue表达在右手边。

的情况下
intVal = foo();

RHS上的表达式是int类型的左值,因此内置的左值到右值转换发生...这几乎不是转换,因为值不会改变,类型也没有(除了基本类型cv-qualifiers被删除之外,因此如果左值是类型const int,则prvalue将是类型int)。 [conv.lval]说

  

非函数非数组类型T的glvalue(3.10)可以转换为prvalue。 [...]如果T是非类类型,则prvalue的类型是T的cv非限定版本。否则,prvalue的类型为T。 [...] glvalue指示的对象中包含的值是prvalue结果。

因此,prvalue的类型为int,其值与foo()相同,即与返回的引用绑定的变量值相同。

赋值表达式的规则说:

  

在简单赋值(=)中,表达式的值替换左操作数引用的对象的值。

因此intVal的值将被prvalue的值替换。规则继续:

  

如果左操作数不是类类型,则表达式被隐式转换(第4节)到左操作数的cv-unqualified类型。

因为int不是类类型(因此没有重载operator=它只使用内置赋值运算符),赋值会将RHS转换为int,是你已经拥有的类型。

因此intVal的值被设置为prvalue的值,我们说的是glvalue表达式foo()的值,即引用绑定到的变量的值。 / p>

请注意,左值到右值的转换与作为参考的RHS无关。同样的事情发生在这里:

int val = 0;
intVal = val;

valint类型的左值,因此它转换为int类型的prvalue,intVal的值设置为该prvalue的值。< / p>

规则用表达式的“值类别”(即左值或右值)表示,而不是它是否为引用。所需的任何“解引用”引用都是由编译器隐式和不可见地完成的,以便实现所需的行为。