我应该返回一个右值引用(通过std :: move'ing)吗? [英] Should I return an rvalue reference (by std::move'ing)?
问题描述
A 如果A
具有可访问的副本或移动构造函数,则编译器可以选择取消副本.否则,如果A
具有移动构造函数,则会移动v
.否则,如果A
具有复制构造函数,则将复制v
.
否则,将发出编译时错误.
If A
has an accessible copy or move constructor, the compiler may choose to elide the copy. Otherwise, if A
has a move constructor, v
is moved. Otherwise, if A
has a copy constructor, v
is copied.
Otherwise, a compile time error is emitted.
我认为我应该始终返回不带std::move
的值
因为编译器将能够为用户找出最佳选择.但是在博客文章的另一个示例中
I thought I should always return the value without std::move
because the compiler would be able to figure out the best choice for users. But in another example from the blog post
Matrix operator+(Matrix&& temp, Matrix&& y)
{ temp += y; return std::move(temp); }
此处std::move
是必需的,因为必须将y
视为函数内的左值.
Here the std::move
is necessary because y
must be treated as an lvalue inside the function.
啊,研究了这篇博文后,我的头几乎炸了起来.我尽了最大的努力去理解推理,但是我研究的越多,我就越困惑.为什么要在std::move
的帮助下返回值?
Ah, my head almost blow up after studying this blog post. I tried my best to understand the reasoning but the more I studied, the more confused I became. Why should we return the value with the help of std::move
?
推荐答案
所以,假设您拥有:
A compute()
{
A v;
…
return v;
}
您正在做:
A a = compute();
此表达式涉及两个传输(复制或移动).首先,必须将函数中用v
表示的对象传送到函数的结果,即compute()
表达式捐赠的值.我们将其称为Transfer1.然后,该临时对象被传输以创建由a
-Transfer 2表示的对象.
There are two transfers (copy or move) that are involved in this expression. First the object denoted by v
in the function must be transferred to the result of the function, i.e. the value donated by the compute()
expression. Let's call that Transfer 1. Then, this temporary object is transferred to create the object denoted by a
- Transfer 2.
在许多情况下,编译器可以忽略传输1和传输2-对象v
是直接在a
的位置构造的,不需要传输.在此示例中,编译器必须使用传输1的命名返回值优化,因为要返回的对象已命名.但是,如果禁用复制/移动省略,则每次传输都涉及对A的复制构造函数或其移动构造函数的调用.在大多数现代编译器中,编译器会看到v
即将被销毁,它将首先将其移到返回值中.然后,此临时返回值将移入a
.如果A
没有移动构造函数,它将被复制以用于两次传输.
In many cases, both Transfer 1 and 2 can be elided by the compiler - the object v
is constructed directly in the location of a
and no transferring is necessary. The compiler has to make use of Named Return Value Optimization for Transfer 1 in this example, because the object being returned is named. If we disable copy/move elision, however, each transfer involves a call to either A's copy constructor or its move constructor. In most modern compilers, the compiler will see that v
is about to be destroyed and it will first move it into the return value. Then this temporary return value will be moved into a
. If A
does not have a move constructor, it will be copied for both transfers instead.
现在让我们看看:
A compute(A&& v)
{
return v;
}
我们返回的值来自传递给函数的引用.编译器不仅认为v
是临时的,而且还可以从v
视为带有std::move
的临时对象. :
The value we're returning comes from the reference being passed into the function. The compiler doesn't just assume that v
is a temporary and that it's okay to move from it1. In this case, Transfer 1 will be a copy. Then Transfer 2 will be a move - that's okay because the returned value is still a temporary (we didn't return a reference). But since we know that we've taken an object that we can move from, because our parameter is an rvalue reference, we can explicitly tell the compiler to treat v
as a temporary with std::move
:
A compute(A&& v)
{
return std::move(v);
}
现在转移1和转移2都将移动.
Now both Transfer 1 and Transfer 2 will be moves.
1 编译器不会自动将定义为A&&
的v
视为右值的原因是安全的.弄清楚它不仅太愚蠢.对象具有名称后,就可以在整个代码中多次引用该对象.考虑:
1 The reason why the compiler doesn't automatically treat v
, defined as A&&
, as an rvalue is one of safety. It's not just too stupid to figure it out. Once an object has a name, it can be referred to multiple times throughout your code. Consider:
A compute(A&& a)
{
doSomething(a);
doSomethingElse(a);
}
如果a
被自动视为右值,则doSomething
可以自由删除其胆量,这意味着传递给doSomethingElse
的a
可能无效.即使doSomething
通过值接受其参数,该对象也会从该位置移出,因此在下一行中无效.为避免此问题,命名的右值引用是左值.这意味着,当调用doSomething
时,即使不是仅由左值引用采用,也会从最差的位置复制a
-在下一行中仍然有效.
If a
was automatically treated as an rvalue, doSomething
would be free to rip its guts out, meaning that the a
being passed to doSomethingElse
may be invalid. Even if doSomething
took its argument by value, the object would be moved from and therefore invalid in the next line. To avoid this problem, named rvalue references are lvalues. That means when doSomething
is called, a
will at worst be copied from, if not just taken by lvalue reference - it will still be valid in the next line.
compute
的作者要说:好吧,现在我允许移去该值,因为我确定这是一个临时对象".您可以说std::move(a)
来完成此操作.例如,您可以给doSomething
一份副本,然后允许doSomethingElse
移出副本:
It is up to the author of compute
to say, "okay, now I allow this value to be moved from, because I know for certain that it's a temporary object". You do this by saying std::move(a)
. For example, you could give doSomething
a copy and then allow doSomethingElse
to move from it:
A compute(A&& a)
{
doSomething(a);
doSomethingElse(std::move(a));
}
这篇关于我应该返回一个右值引用(通过std :: move'ing)吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!