C++ 什么时候应该在函数返回值上使用 std::move ?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14856344/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When should std::move be used on a function return value?
提问by user2015453
In this case
在这种情况下
struct Foo {};
Foo meh() {
return std::move(Foo());
}
I'm pretty sure that the move is unnecessary, because the newly created Foo
will be an xvalue.
我很确定移动是不必要的,因为新创建的Foo
将是 xvalue。
But what in cases like these?
但是在这种情况下怎么办?
struct Foo {};
Foo meh() {
Foo foo;
//do something, but knowing that foo can safely be disposed of
//but does the compiler necessarily know it?
//we may have references/pointers to foo. how could the compiler know?
return std::move(foo); //so here the move is needed, right?
}
There the move is needed, I suppose?
有必要搬家,我想?
回答by Steve Jessop
In the case of return std::move(foo);
the move
is superfluous because of 12.8/32:
在的情况下return std::move(foo);
的move
是多余的,因为12.8 / 32:
When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is ?rst performed as if the object were designated by an rvalue.
当满足或将满足复制操作的省略条件时,除了源对象是函数参数,并且要复制的对象由左值指定时,选择复制构造函数的重载决议是第一次执行,就好像对象是由右值指定的一样。
return foo;
is a case of NRVO, so copy elision is permitted. foo
is an lvalue. So the constructor selected for the "copy" from foo
to the return value of meh
is required to be the move constructor if one exists.
return foo;
是 NRVO 的一种情况,因此允许复制省略。foo
是一个左值。因此,如果存在foo
,meh
则为“复制”到 的返回值而选择的构造函数必须是移动构造函数。
Adding move
does have a potential effect, though: it prevents the move being elided, because return std::move(foo);
is noteligible for NRVO.
但是,添加move
确实有潜在的影响:它可以防止移动被忽略,因为return std::move(foo);
它不符合 NRVO 的条件。
As far as I know, 12.8/32 lays out the onlyconditions under which a copy from an lvalue can be replaced by a move. The compiler is not permitted in general to detect that an lvalue is unused after the copy (using DFA, say), and make the change on its own initiative. I'm assuming here that there's an observable difference between the two -- if the observable behavior is the same then the "as-if" rule applies.
据我所知,12.8/32 列出了可以用移动替换左值副本的唯一条件。编译器通常不允许在复制后检测到左值未使用(例如使用 DFA),并主动进行更改。我在这里假设两者之间存在可观察到的差异——如果可观察到的行为相同,则“as-if”规则适用。
So, to answer the question in the title, use std::move
on a return value when you want it to be moved and it would not get moved anyway. That is:
因此,要回答标题中的问题,请在std::move
希望移动返回值并且无论如何都不会移动时使用返回值。那是:
- you want it to be moved, and
- it is an lvalue, and
- it is not eligible for copy elision, and
- it is not the name of a by-value function parameter.
- 你希望它被移动,并且
- 它是一个左值,并且
- 它不符合复制省略的条件,并且
- 它不是按值函数参数的名称。
Considering that this is quite fiddly and moves are usuallycheap, you might like to say that in non-template code you can simplify this a bit. Use std::move
when:
考虑到这非常繁琐,而且移动通常很便宜,您可能想说,在非模板代码中,您可以稍微简化一下。std::move
在以下情况下使用:
- you want it to be moved, and
- it is an lvalue, and
- you can't be bothered worrying about it.
- 你希望它被移动,并且
- 它是一个左值,并且
- 你不必担心它。
By following the simplified rules you sacrifice some move elision. For types like std::vector
that are cheap to move you'll probably never notice (and if you do notice you can optimize). For types like std::array
that are expensive to move, or for templates where you have no idea whether moves are cheap or not, you're more likely to be bothered worrying about it.
通过遵循简化的规则,您牺牲了一些移动省略。对于这样的类型std::vector
,移动起来很便宜,你可能永远不会注意到(如果你注意到了,你可以优化)。对于std::array
此类移动成本高昂的类型,或者对于您不知道移动是否便宜的模板,您更有可能为此烦恼。
回答by Andy Prowl
The move is unnecessary in both cases. In the second case, std::move
is superfluous because you are returning a local variable by value, and the compiler will understand that since you're not going to use that local variable anymore, it can be moved from rather than being copied.
在这两种情况下都不需要移动。在第二种情况下,std::move
是多余的,因为您按值返回局部变量,编译器会理解,因为您不再使用该局部变量,因此可以将其移出而不是被复制。
回答by David Rodríguez - dribeas
On a return value, if the return expression refers directly to the name of a local lvalue (i.e. at this point an xvalue) there is no need for the std::move
. On the other hand, if the return expression is notthe identifier, it will not be moved automatically, so for example, you would need the explicit std::move
in this case:
在返回值上,如果返回表达式直接引用本地左值的名称(即此时是 xvalue),则不需要std::move
. 另一方面,如果返回表达式不是标识符,它不会自动移动,因此例如,std::move
在这种情况下您需要显式:
T foo(bool which) {
T a = ..., b = ...;
return std::move(which? a : b);
// alternatively: return which? std::move(a), std::move(b);
}
When returning a named local variable or a temporary expression directly, you should avoid the explicit std::move
. The compiler must(and will in the future) move automatically in those cases, and adding std::move
might affect other optimizations.
直接返回命名局部变量或临时表达式时,应避免使用显式std::move
. 在这些情况下,编译器必须(并且将来会)自动移动,并且添加std::move
可能会影响其他优化。
回答by Yakk - Adam Nevraumont
There are lots of answers about when it shouldn't be moved, but the question is "when should it be moved?"
关于什么时候不应该移动有很多答案,但问题是“什么时候应该移动?”
Here is a contrived example of when it should be used:
这是一个应该何时使用它的人为示例:
std::vector<int> append(std::vector<int>&& v, int x) {
v.push_back(x);
return std::move(v);
}
ie, when you have a function that takes an rvalue reference, modifies it, and then returns a copy of it. Now, in practice, this design is almost always better:
即,当您有一个接受右值引用的函数时,修改它,然后返回它的副本。现在,在实践中,这种设计几乎总是更好:
std::vector<int> append(std::vector<int> v, int x) {
v.push_back(x);
return v;
}
which also allows you to take non-rvalue parameters.
它还允许您采用非右值参数。
Basically, if you have an rvalue reference within a function that you want to return by moving, you have to call std::move
. If you have a local variable (be it a parameter or not), returning it implicitly move
s (and this implicit move can be elided away, while an explicit move cannot). If you have a function or operation that takes local variables, and returns a reference to said local variable, you have to std::move
to get move to occur (as an example, the trinary ?:
operator).
基本上,如果您想通过移动返回的函数中有一个右值引用,则必须调用std::move
. 如果您有一个局部变量(无论它是否是参数),则隐式返回它move
s(并且可以省略此隐式移动,而显式移动则不能)。如果您有一个函数或操作接受局部变量,并返回对所述局部变量的引用,则必须std::move
让 move 发生(例如,三元?:
运算符)。
回答by atomsymbol
A C++ compiler is free to use std::move(foo)
:
C++ 编译器可以免费使用std::move(foo)
:
- if it is known that
foo
is at the end of its lifetime, and - the implicit use of
std::move
won't have any effect on the semantics of the C++ code other than the semantic effects allowed by the C++ specification.
- 如果知道
foo
它的生命周期结束,并且 std::move
除了 C++ 规范允许的语义效果之外,隐式使用不会对 C++ 代码的语义产生任何影响。
It depends on the optimization capabilities of the C++ compiler whether it is able to compute which transformations from f(foo); foo.~Foo();
to f(std::move(foo)); foo.~Foo();
are profitable in terms of performance or in terms of memory consumption, while adhering to the C++ specification rules.
这取决于 C++ 编译器的优化能力,它是否能够在遵守 C++ 规范规则的同时,计算出哪些从f(foo); foo.~Foo();
到 的转换f(std::move(foo)); foo.~Foo();
在性能或内存消耗方面是有利可图的。
Conceptuallyspeaking, year-2017 C++ compilers, such as GCC 6.3.0, are able to optimizethis code:
从概念上讲,2017 年的 C++ 编译器,例如 GCC 6.3.0,能够优化此代码:
Foo meh() {
Foo foo(args);
foo.method(xyz);
bar();
return foo;
}
into this code:
进入这个代码:
void meh(Foo *retval) {
new (retval) Foo(arg);
retval->method(xyz);
bar();
}
which avoids calling the copy-constructor and the destructor of Foo
.
这避免了调用 的复制构造函数和析构函数Foo
。
Year-2017 C++ compilers, such as GCC 6.3.0, are unable to optimizethese codes:
2017 年的 C++ 编译器,例如 GCC 6.3.0,无法优化这些代码:
Foo meh_value() {
Foo foo(args);
Foo retval(foo);
return retval;
}
Foo meh_pointer() {
Foo *foo = get_foo();
Foo retval(*foo);
delete foo;
return retval;
}
into these codes:
进入这些代码:
Foo meh_value() {
Foo foo(args);
Foo retval(std::move(foo));
return retval;
}
Foo meh_pointer() {
Foo *foo = get_foo();
Foo retval(std::move(*foo));
delete foo;
return retval;
}
which means that a year-2017 programmer needs to specify such optimizations explicitly.
这意味着 2017 年的程序员需要明确指定此类优化。
回答by Robert Allan Hennigan Leahy
std::move
is totally unnecessary when returning from a function, and really gets into the realm of you -- the programmer -- trying to babysit things that you should leave to the compiler.
std::move
从函数返回时完全没有必要,并且真正进入了您 - 程序员 - 试图照顾您应该留给编译器的事情的领域。
What happens when you std::move
something out of a function that isn't a variable local to that function? You can say that you'll never write code like that, but what happens if you write code that's just fine, and then refactor it and absent-mindedly don't change the std::move
. You'll have fun tracking that bug down.
当你std::move
从一个函数中提取的东西不是该函数的局部变量时会发生什么?你可以说你永远不会写那样的代码,但是如果你写的代码很好,然后重构它并且心不在焉地不更改std::move
. 你会很高兴追踪那个错误。
The compiler, on the other hand, is mostly incapable of making these kinds of mistakes.
另一方面,编译器大多无法犯这些错误。
Also: Important to note that returning a local variable from a function does notnecessarily create an rvalue or use move semantics.
另外:需要注意的是从函数返回一个局部变量并没有必要创建一个右值或使用移动语义。