1

在我看来,能够将 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()不是,即使这两个函数在逻辑上看起来是等价的。是什么赋予了?

4

1 回答 1

4

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

右值和左值之间的关键概念区别在于您只能访问一次右值,这使得对它做奇怪的事情是安全的。但是,如果该访问将其绑定到右值引用,则您只是创建了一种多次访问它的方法,即您将其转换为左值。所以你不能将它隐式绑定到另一个右值引用,而你可以将它绑定到一个左值引用。

编辑:

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

另一方面,您将右值绑定到右值引用。foo,通过获取右值引用,宣布它可能会修改传递的对象,但这是一种性能优化,并不意味着外部任何人都可以观察到。传递一个临时变量是有道理的,但传递一个非临时变量是非常危险的,这就是为什么std::move在这种情况下你必须明确地论证的原因。

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

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

于 2013-10-22T15:13:12.397 回答