我已经编写了以下示例:
#include <iostream>
volatile int&& bar()
{
return 1;
}
int main()
{
const int& i = bar(); //error: binding of reference to type 'const int'
//to a value of type 'volatile int' drops qualifiers
}
演示
但如果我们用int
替换int&&
,它就可以正常工作:
#include <iostream>
volatile int bar()
{
return 1;
}
int main()
{
const int& i = bar(); //OK
}
演示
这还不完全清楚。标准所说的是(8.5.3/5 [dcl.init.ref]
):
对类型"cv1T1"的引用由类型的表达式初始化"cv2 T2"如下:
--如果引用是左值引用,并且初始值设定项表达式
是一个左值(但不是位字段),"cv1T1"是与"cv2T2"或兼容的参考
具有类类型(即T2是类类型),其中T1不是与T2相关的引用,并且可以转换为类型为"cv3T3"的左值,其中"cv1T1"与"cv3 T3"108(通过列举适用的转换函数来选择此转换(13.3.1.6),并通过过载分辨率(13.3)选择最佳值),然后引用绑定到第一个中的初始值设定项表达式lvalue在第二种情况下转换为左值结果(或者,在任何一种情况下,都指向对象)。
[…]
--否则,引用应为对非易失性常量类型(即,cv1应为常量),或引用应为右值参考
在第一个例子中,我们有一个类型为volatile int&&
的右值。并且'otherwise'
的情况适用于这两个例子。但5/5 [expr]
说:
如果一个表达式最初具有"对T的引用"类型(8.3.2,8.5.3),在任何进一步分析之前,将类型调整为T
因此,本质上,我们有一个类型为volatile int
而不是volatile int&&
的右值,这意味着这两个例子将以相同的方式工作。
这两种情况的区别在于,在情况1中,引用直接绑定,而在情况2中,它不直接绑定(即引用绑定到临时;定义可以在[dcl.init.ref]
的最后一段中找到)。
直接绑定失败,因为T2是易失性限定的,而T1不是(在标准术语中,T1与T2不兼容)。
间接绑定之所以成功,是因为当从bar()
返回的引用初始化临时int
时,该临时不是volatile
。(临时的类型为cv1 T1)。
了解为什么案例1是直接绑定。首先,bar()
在这里是一个xvalue。参见CCD_ 14〃;调用返回类型为右值引用的函数的结果是xvalue";。
来自[dcl.init.ref]/5
:
- 如果引用是左值引用,并且初始值设定项表达式[是左值]或[具有类类型]
不适用:初始值设定项是一个xvalue(而不是lvalue),它是一个引用,因此没有类类型。
- 否则,引用应为非易失常量类型的左值引用(即,cv1应为常量),或者引用应为右值引用
这确实适用:const int &
是对非易失性常量类型的左值引用。沿着这棵树走下去:
如果初始值设定项表达式
- 是xvalue(但不是位字段)、类prvalue、数组prvalue或函数lvalue,并且"cv1 T1"是与"cv2 T2"兼容的引用,或者
- [另一个案例]
则在第一种情况下引用被绑定到初始值设定项表达式的值[…]
这确实适用,因为bar()
是一个x值。因此,引用绑定到xvalue,这被称为直接绑定,因为它没有绑定到临时绑定。
注:。所有标准参考均来自C++14(N3936)。由于DR1288,本节从C++11更改为。