【问题标题】:Why both const/nonconst lvalue references bind to a rvalue reference?为什么 const/nonconst 左值引用都绑定到右值引用?
【发布时间】:2017-04-15 23:59:52
【问题描述】:

允许使用这个非常简单的代码:

class s
{
public:
};

const s& tt = std::move(s()); // Also valid without 'const'

但现在我想知道为什么允许这样做..

首先我们使用std::move并将右值(临时)标记为右值引用,但是为什么左值引用可以绑定到右值引用?

是不是因为右值引用也是右值?

导致左值引用绑定到右值引用的基本原理(或标准引号)是什么?可以做到我明白了。

编辑:msvc2015 允许非常量左值引用绑定到右值引用,问题仍然是 const 左值引用绑定到右值引用。抱歉,我应该指定我使用的编译器。

【问题讨论】:

  • 我发现这个问题有点难以解析,因为您使用的术语错误:在T & t = u;,我们说“(参考)t 绑定到值@987654326 @。”此外,请注意,引用总是绑定到 values;引用不会“绑定到另一个引用”。引用类型总是与变量(或函数)相连,从不与表达式相连。在通常使用的术语中,问题可能会变成“为什么左值引用绑定到右值?”,这是一个很好的问题(答案很简单)。
  • @KerrekSB 糟糕,我编辑了问题以(希望)使用正确的术语
  • can not compile 没有const
  • @ElProfesor: 嗯...标准通常说“引用绑定到表达式”,实际上。
  • @ElProfesor:是的,我想这是真的 :-)

标签: c++ rvalue-reference rvalue


【解决方案1】:

作为标准转换之一(C++ 标准的第 4 章),右值引用被隐式转换为右值(更具体地说,转换为 xvalue):

任何隐式转换的效果都与执行 相应的声明和初始化,然后使用 作为转换结果的临时变量。 结果是 如果 T 是左值引用类型或右值引用,则为左值 函数类型 (8.3.2),如果 T 是对对象的右值引用,则为 xvalue 类型,否则为纯右值

Rvalues(包括xvalues)可以绑定到const lvalue 引用,这样您就可以将临时值传递给具有此类参数的函数:

void foo(const bar &a);
// ...
foo(bar());

(临时是一个右值;在这种情况下,bar() 的结果是一个右值)。没有理由 允许这样做,因为临时变量总是与包含表达式(在本例中为函数调用)一样长,因此它不会在 foo 内创建悬空引用。

这意味着始终可以将函数fun(bar) 的签名调整为fun(const bar &) - 当然也可以相应地更改实现! - 因为临时参数仍然会被接受,并且从调用者的角度来看语义应该是相同的; const 表示对象不会被修改,复制传递也是如此。

不允许非const 引用;一个实际的原因是因为它们暗示应该以某种有意义的方式修改该值,并且如果该值是临时的,则它将丢失。但是,如果您真的想这样做,您可以将右值转换为左值,但有一些注意事项,如 this answer 中的另一个问题所述。

允许右值绑定到const 左值引用,除了允许通过引用传递临时参数之外,对于确切的参数类型未知但您希望允许移动语义(如果它是)的情况也很有用可能的。假设我正在调用一个可以定义为foo2(const bar &)foo2(bar) 的函数,并且在前一种情况下可能有也可能没有重载foo2(bar &&),并且我希望在可能的情况下允许使用移动语义(假设foo2(bar &&) 重载将在其实现中使用移动语义);我可以安全地使用std::move 创建一个右值,因为它适用于任何一种情况。这个例子可能看起来有点做作,但在编写模板时可能会出现很多情况。在代码中:

bar bb = bar();
foo2(std::move(bb));
// above is legal if foo2 is declared as either:
//    foo2(bar)
//    foo2(const bar &)
// and in latter case calls overload foo2(bar &&) if it is defined.

在其他 rvalue-to-lvalue-reference 赋值涉及临时的情况下,临时的生命周期被延长到引用的生命周期,因此即使在参数传递以外的上下文中也不会创建悬空引用:

const bar &b = bar(); // temporary lifetime is extended

在上面,bar 对象在引用 b 超出范围之前不会被销毁。

【讨论】:

  • 这是一个很好的答案,但我觉得它错过了我的问题的重点:导致左值引用绑定到右值引用的基本原理(或标准引号)是什么??可以做到我明白了。
  • @Dean 这样你就可以将一个临时值传递给一个接受 const 左值引用的函数,这样从调用者的角度来看,foo(const bar &) 和 @ 之间没有语义差异987654342@。答案尽在其中。
  • @Dean 还有:“没有理由允许这样做”......即你为什么认为右值不应该能够绑定到左值引用吗?
  • 我说的是右值references。右值引用是右值吗?如果是这样,这就是我想阅读的答案
  • @Dean 右值引用被隐式转换为右值(具体而言,转换为 xvalues)。您想要的标准部分是“4.标准转换”[conv]。 任何隐式转换的效果都与执行相应的声明和初始化,然后使用临时变量作为转换的结果相同。如果 T 是左值引用类型或对函数类型 (8.3.2) 的右值引用,则结果为左值,如果 T 是对对象类型的右值引用,则为 xvalue,否则为纯右值。
猜你喜欢
  • 2017-04-13
  • 2018-01-13
  • 2021-12-25
  • 1970-01-01
  • 2014-01-02
  • 1970-01-01
  • 1970-01-01
  • 2014-10-31
相关资源
最近更新 更多