为什么我们不能使用rvalue volatile int&&初始化一个const int的引用?

4
我写了以下的例子:
#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" 兼容,或

  • 具有类类型(i.e., T2 is a class type),其中 T1 与 T2 不相关, 并且可以转换为类型为 "cv3 T3" 的左值,其中 "cv1 T1" 和 "cv3 T3" 兼容 108 (通过枚举适用的转换函数(13.3.1.6)和 通过重载分辨率(13.3)选择最佳函数来选择此转换), 那么引用绑定到第一种情况下的初始化表达式左值, 并绑定到第二种情况下的转换结果左值(或在任一情况下, 绑定到对象的适当基类子对象)。

[...]

- 否则,引用应是左值对于非易失性 const 类型(即 cv1 应该是 const), 或者引用应该是右值引用。

在第一个示例中,我们有类型为 volatile int&& 的右值。而5/5 [expr]说:

如果表达式最初具有类型 "T 的引用"(8.3.2、8.5.3),则在进一步分析之前将类型调整为 T。

因此,在本质上,我们有类型为 volatile int 的右值,而不是 volatile int&&,这意味着这两个示例应以相同的方式工作。

1个回答

3
两种情况的区别在于Case 1中引用直接绑定,而在Case 2中它没有直接绑定(即引用绑定到临时变量;定义可在[dcl.init.ref]的最后一段找到)。
直接绑定失败是因为T2带有volatile限定,而T1没有(在标准术语中,T1与T2不兼容)。
间接绑定成功是因为当由bar()返回的引用初始化一个临时int时,该临时变量不是volatile的。(临时变量的类型为cv1 T1)。
要看为什么Case 1是直接绑定,请先看这里:bar()在此处是一种xvalue。请参阅[basic.lval]/1:“调用其返回类型为rvalue引用的函数的结果是xvalue”。
从[dcl.init.ref]/5中得知:
如果引用是左值引用并且初始化表达式是左值或具有类类型,
不适用:初始化器是xvalue(而不是左值),它是引用,因此它没有类类型。
否则,引用应是指向非易失性const类型(即cv1应为const)的左值引用,或者引用应是右值引用。
这适用于:const int&是指向非易失性const类型的左值引用。在这个树下进行:
如果初始化器表达式
是xvalue(但不是位域)、类prvalue、数组prvalue或函数lvalue,并且“cv1 T1”与“cv2 T2”兼容,或
[另一种情况]
则在第一种情况下,将引用绑定到初始化器表达式的值。
这适用于,因为bar()是xvalue。所以引用被绑定到xvalue,这被称为直接绑定,因为它没有绑定到一个临时变量。
注:所有标准参考都来自C++14(N3936)。由于DR1288,此部分从C++11更改。

1
此外,如果我没有记错的话,在这种情况下返回的 rvalue 将是一个悬空引用,因为创建引用的 temp 在赋值 i = bar() 后立即被销毁。为什么不直接通过值返回,让编译器进行优化(省略)或使用移动构造函数呢? - victor
在您的第二个例子中,bar() 是一个 prvalue;在第一个例子中它是一个 xvalue。这并不是很清楚。您如何区分这些? - user2953119
@MattMcNabb 这就是我说的:“在赋值 i = bar() 之后立即销毁”,这也是临时对象被销毁的时候 :) 我不是100%确定,但我认为这很有道理。 - victor
@victor 实际上进一步检查后,它会在函数返回时立即被销毁(比我们之前说的还要早)。 - M.M
这个答案有一个问题。http://stackoverflow.com/questions/35322089/const-and-volatile-are-reference-compatible - user2486888
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接