从r值ref转换为l值ref,为什么它有效?

时间:2013-10-22 15:08:16

标签: c++ c++11

在我看来,能够将r值引用转换为l值引用是没有意义的,这使得最终能够将临时访问作为l值。例如:

struct S
{
  int val=5;
};

void foo(S &&s)
{
  S &s2=s;

  s2.val=7
}

void foo2(S &s2)
{
  s2.val=7
}

int main()
{
  foo(S());  // This is fine
  foo2(S()); // Error: Even though this is logically equivalent to foo()
}

foo()的调用似乎在C ++中有效,或者至少用gcc 4.7.1编译。对foo2()的调用不是,即使这两个函数在逻辑上是等价的。是什么给了什么?

1 个答案:

答案 0 :(得分:4)

s已经是一个左值,就像任何其他命名变量一样。因此,对它进行左值引用并没有真正改变任何东西。

右值和左值之间的关键概念差异在于,您只能访问一次右值,这样可以安全地对它进行奇怪的处理。但是如果那个访问将它绑定到右值引用,那么你刚刚创建了一种方法来多次访问它,即你把它变成了左值。所以你不能隐式地将它绑定到另一个右值引用,而你可以将它绑定到左值引用。

修改

这两件事 在逻辑上是等价的。在一个中,将rvalue绑定到左值引用。这是不允许的,因为它具有误导性。 (并不像允许左值绑定到右值引用那样危险,但仍然具有误导性。)foo2,通过左值引用,宣布它可能会修改传递的对象,这绝对是一个中心部分函数的操作。将临时函数传递给这样的函数没有意义,因为您无法观察到修改。

另一方面,您将右值绑定到右值引用。 foo,通过采用右值引用,宣布它可能会修改传递的对象,但这是性能优化,并不是任何人都可以观察到的。传递一个临时文字是有道理的,但传递非临时文件会非常危险,这就是为什么在这种情况下你必须明确地std::move参数。

然后,在foo中,将左值(它恰好是指向rvalue引用类型的变量的表达式,但这是无关的)绑定到左值表达式。这是有道理的:你已经得到了你的右值引用变量s,它指向了一些东西,它已经被传递给你的函数,已成为你的玩物。因此,如果您想将其传递给修改其状态的内容(例如foo2),然后观察更改(您可以通过ss2两者),这在概念上是完美的声音。

基本上,只能观察一次或多次的事情是一个范围问题。这就是为什么“相同”的东西(它不是真的是同一个东西,但你想到的那种方式)可以绑定到一个上下文中的右值引用,以及另一个中的左值引用。