为什么我们不能用右值易失性 int&&&初始化对 const int 的引用?



我已经编写了以下示例:

#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不是(在标准术语中,T1T2不兼容)。

间接绑定之所以成功,是因为当从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更改为。

最新更新