所以,让我们说你有:
A compute()
{
A v;
…
return v;
}
而你正在做的:
A a = compute();
有两种传输(复制或移动)中涉及的这个表达。首先,函数中由v
表示的对象必须被转移到该函数的结果,即由compute()
表达式贡献的值。我们称之为Transfer 1.然后,这个临时对象被转移来创建a
表示的对象 - 传输2.
在很多情况下,编译器可以忽略Transfer 1和2 - 直接构造对象v
在a
的位置,并且不需要转移。在这个例子中,编译器必须使用Named Return Value Optimization for Transfer 1,因为返回的对象是被命名的。但是,如果我们禁用复制/移动省略,则每次传输都会调用A的拷贝构造函数或其移动构造函数。在大多数现代编译器中,编译器将看到v
即将被销毁,它将首先将其移入返回值。然后这个临时返回值将被移至a
。如果A
没有移动构造函数,它将被复制用于两次传输。
现在让我们看一下:
A compute(A&& v)
{
return v;
}
我们返回的值来自参考被传递给函数。编译器不只是假设v
是一个临时的,它可以从它移动。在这种情况下,Transfer 1将成为副本。然后转移2将是一个举动 - 没关系,因为返回的值仍然是一个临时的(我们没有返回一个引用)。但由于我们知道,我们已经采取了对象,我们可以从移动,因为我们的参数是一个右值引用,我们可以明确地告诉编译器把v
作为临时与std::move
:
A compute(A&& v)
{
return std::move(v);
}
现在转移1和转移2将会移动。
之所以编译器不会自动把v
,定义为A&&
,作为右值是安全的一个。弄清楚这不仅仅是太愚蠢。一旦一个对象有一个名字,它可以在整个代码中被多次引用。试想一下:
A compute(A&& a)
{
doSomething(a);
doSomethingElse(a);
}
如果a
作为右值是自动处理,doSomething
可以自由地撕裂它的胆量了,这意味着a
传递给doSomethingElse
可能无效。即使doSomething
通过值取其参数,该对象也将从下一行中移出,因此无效。为了避免这个问题,指定的右值引用是左值。这意味着当doSomething
被调用时,a
最坏的情况下将被复制,如果不是仅由左值引用 - 它将在下一行仍然有效。
这是由作者compute
说,“好吧,现在我允许这个值被移出,因为我确定它是一个临时对象”。你可以这么说std::move(a)
。例如,你可以给doSomething
副本,然后让doSomethingElse
从中移动:
A compute(A&& a)
{
doSomething(a);
doSomethingElse(std::move(a));
}
你有一个链接到有问题的文章? –
[相关常见问题](http://stackoverflow.com/questions/3106110/) – fredoverflow
http://cpp-next.com/archive/2009/09/making-your-next-move/,这是一个系列右值引用 – StereoMatching