5

以下 C++11 代码无法编译:

struct T {};

void f(T&&) { }

void g(T&& t) { f(t); }

int main()
{
    g(T());
}

正确的方法是:

void g(T&& t) { f(move(t)); }

这很难用正确的自然语言术语来解释。该参数t似乎失去了它需要恢复的“&&”状态std::move

你叫T()什么g(T())

你叫T&&什么g(T&& t)

你叫t什么g(T&& t)

你怎么称呼tinf(t)f(move(t))

什么叫返回值move(t)

什么叫整体效果?

标准的哪些部分处理这个问题?

4

5 回答 5

7

所有参数都是左值,即使它们的类型是“右值引用”。它们有名称,因此您可以根据需要随时引用它们。如果命名的右值引用是右值,你会得到令人惊讶的行为。我们不希望从左值进行隐式移动,这就是您必须显式编写std::move.

于 2013-01-06T18:16:15.847 回答
7

关键是一个参数T&& b只能绑定到一个右值,但是当后面提到时,表达式b是一个左值。

所以函数的参数必须是一个右值,但在函数体内,参数是一个左值,因为到那时你已经绑定了一个对它的引用并给它一个名字,它不再是一个未命名的临时值。

表达式有一个类型(例如intstring),它有一个值类别(例如左值或右值),这两个东西是不同的。

一个被声明为T&& b“rvalue reference to T”类型的命名变量,并且只能绑定到一个rvalue,但是当您稍后使用该引用时,表达式b具有值类别“lvalue”,因为它有一个名称并引用了某个对象(无论引用绑定到什么,即使那是一个右值。)这意味着传递b给另一个函数,该函数接受一个右值,你不能只说f(b)因为b它是一个左值,所以你必须将它(返回)转换为右值,通过std::move(b).

于 2013-01-06T18:19:34.407 回答
5

g(T()) 中的 T() 叫什么?

临时的(可移动的)。

您将 g(T&& t) 中的 T&& 称为什么?

T&& 是一个右值引用,代表一个可以移动的对象。

g(T&& t) 中的 t 叫什么?

t 实际上是一个左值,因为您可以通过名称来引用它。

您将 f(t) 和 f(move(t)) 中的 t 称为什么?

  1. 左值
  2. 当 move() 返回时,左值被转换为右值引用

你把 move(t) 的返回值叫做什么?

r 值参考

作为说明;您也许应该调用结构 C,并编写一个单独的示例,其中 T 实际上是模板化的。那么代码需要有所不同,因为在一个函数中template< typename T > void f( T&& t );你不能简单地使用std::move()而不非常小心,因为 T 实际上可以是 a const&,在这种情况下你不能使用std::move()而是使用完美转发std::forward< T >( t )

于 2013-01-06T18:15:36.353 回答
4

g(T()) 中的 T() 叫什么?

那是一个临时对象和一个 r 值。

您将 g(T&& t) 中的 T&& 称为什么?

你称之为右值参考。

之所以

void g(T&& t) { f(t); }

不起作用是因为 r 值引用无法绑定到命名对象(即使该命名对象恰好是另一个 r 值引用)。

于 2013-01-06T18:17:02.210 回答
1

该参数t不会丢失其“状态”。简单地说,参数t是一个左值,即使它是一个右值引用。请记住,左值和右值是正交概念,并且适用于值引用(包括右值引用)的值。因此,右值引用可以是左值或右值。如果它有一个名称,如您的示例所示,它是一个左值。这使得类型系统正交,恕我直言,这是一个很好的功能。

于 2013-01-06T18:16:48.867 回答