我写了以下例子:
#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]
):
对类型“cv1 T1”的引用由类型的表达式初始化 “cv2 T2”如下:
- 如果引用是左值引用而且是 初始化表达式
- 引用兼容
是左值(但不是位字段),“cv1 T1”与“cv2 T2”或
有一个类类型(即T2是类类型),其中T1与T2没有引用相关, 并且可以转换为“cv3 T3”类型的左值,其中“cv1 T1”与参考兼容 “cv3 T3”108(通过枚举适用的转换函数来选择此转换 (13.3.1.6)并通过重载决议(13.3)选择最佳的一个,然后是 引用绑定到第一个初始化表达式lvalue case和第二种情况下转换的左值结果 (或者,在任何一种情况下,到相应的基类子对象 宾语)。
[...]
- 否则,引用应为的左值引用 非易失性const类型(即cv1应为const)或引用 应为右值参考。
嗯,在第一个例子中,我们有一个volatile int&&
类型的右值。 'otherwise'
案例适用于两个例子。但5/5 [expr]
说:
如果表达式最初具有“引用T”类型(8.3.2, 8.5.3),在进一步分析之前将类型调整为T
所以,基本上我们有一个volatile int
而不是volatile int&&
的右值,这意味着这两个例子都应该以相同的方式工作。
答案 0 :(得分:3)
两种情况之间的区别在于,在案例1中,引用直接绑定,在案例2中,它不直接绑定(即引用绑定到临时;定义可以在[dcl.init.ref]
)的最后一段。
直接绑定失败,因为 T2 是volatile限定的而 T1 不是(在标准术语中, T1 不是参考兼容与 T2 )。
间接绑定成功,因为从int
返回的引用初始化临时bar()
时,临时不是volatile
。 (临时类型为 cv1 T1 )。
了解为什么案例1是直接绑定。首先,此处bar()
是 xvalue 。请参阅[basic.lval]/1
“调用返回类型为右值引用的函数的结果是xvalue”。
来自[dcl.init.ref]/5
:
- 如果引用是左值引用且初始化表达式[是左值]或[具有类类型]
不适用:初始值设定项是xvalue(不是左值),它是一个引用,因此它没有类类型。
- 否则,引用应该是对非易失性const类型的左值引用(即,cv1应为const),或者引用应为右值引用
这确实适用:const int &
是对非易失性const类型的左值引用。走下这棵树:
如果是初始化表达式
- 是xvalue(但不是位字段),类prvalue,数组prvalue或函数lvalue,“cv1 T1”与“cv2 T2”引用兼容,或
- [另一个案例]
然后引用绑定到第一种情况[...]
中的初始化表达式的值
这确实适用,因为bar()
是xvalue。因此引用绑定到xvalue,这称为直接绑定,因为它没有绑定到临时值。
NB。所有标准参考均来自C ++ 14(N3936)。由于DR1288,此部分已从C ++ 11更改。