在我看来,能够将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()
的调用不是,即使这两个函数在逻辑上是等价的。是什么给了什么?
答案 0 :(得分:4)
s
已经是一个左值,就像任何其他命名变量一样。因此,对它进行左值引用并没有真正改变任何东西。
右值和左值之间的关键概念差异在于,您只能访问一次右值,这样可以安全地对它进行奇怪的处理。但是如果那个访问将它绑定到右值引用,那么你刚刚创建了一种方法来多次访问它,即你把它变成了左值。所以你不能隐式地将它绑定到另一个右值引用,而你可以将它绑定到左值引用。
修改强>
这两件事 在逻辑上是等价的。在一个中,将rvalue绑定到左值引用。这是不允许的,因为它具有误导性。 (并不像允许左值绑定到右值引用那样危险,但仍然具有误导性。)foo2
,通过左值引用,宣布它可能会修改传递的对象,这绝对是一个中心部分函数的操作。将临时函数传递给这样的函数没有意义,因为您无法观察到修改。
另一方面,您将右值绑定到右值引用。 foo
,通过采用右值引用,宣布它可能会修改传递的对象,但这是性能优化,并不是任何人都可以观察到的。传递一个临时文字是有道理的,但传递非临时文件会非常危险,这就是为什么在这种情况下你必须明确地std::move
参数。
然后,在foo
中,将左值(它恰好是指向rvalue引用类型的变量的表达式,但这是无关的)绑定到左值表达式。这是有道理的:你已经得到了你的右值引用变量s
,它指向了一些东西,它已经被传递给你的函数,已成为你的玩物。因此,如果您想将其传递给修改其状态的内容(例如foo2
),然后观察更改(您可以通过s
或s2
两者),这在概念上是完美的声音。
基本上,只能观察一次或多次的事情是一个范围问题。这就是为什么“相同”的东西(它不是真的是同一个东西,但你想到的那种方式)可以绑定到一个上下文中的右值引用,以及另一个中的左值引用。