什么时候应该在函数返回值上使用std: move ?(复制)

时间:2022-09-10 23:48:53

This question already has an answer here:

这个问题已经有了答案:

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?

我想我们需要搬家了吧?

6 个解决方案

#1


95  

In the case of return std::move(foo); the move is superfluous because of 12.8/32:

如果返回std::move(foo);这一举动是多余的,因为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 first 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.

返回foo;是NRVO的病例,所以拷贝省略是允许的。foo是一个左值。因此,为foo中的“copy”选择的构造函数到meh的返回值,如果存在,则必须是move构造函数。

Adding move does have a potential effect, though: it prevents the move being elided, because return std::move(foo); is not eligible for NRVO.

添加move确实有一个潜在的影响:它阻止移动被省略,因为返回std::move(foo);不符合NRVO要求。

As far as I know, 12.8/32 lays out the only conditions 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列出了唯一的条件,在此条件下,lvalue的副本可以被移动取代。一般来说,编译器不允许在复制之后检测lvalue是否未使用(例如使用DFA),并主动进行更改。我假设这两者之间有一个可观察的区别——如果可观察的行为是相同的那么“假设”规则就适用。

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:::当您希望它移动时,移动一个返回值,并且无论如何它不会移动。那就是:

  • you want it to be moved, and
  • 你希望它被移动
  • it is an lvalue, and
  • 它是一个lvalue,并且
  • it is not eligible for copy elision, and
  • 它不符合复制elision,和。
  • it is not the name of a by-value function parameter.
  • 它不是按值函数参数的名称。

Considering that this is quite fiddly and moves are usually cheap, you might like to say that in non-template code you can simplify this a bit. Use std::move when:

考虑到这相当复杂,而且移动通常很便宜,您可能想说,在非模板代码中,您可以稍微简化一下。使用std::移动时:

  • you want it to be moved, and
  • 你希望它被移动
  • it is an lvalue, and
  • 它是一个lvalue,并且
  • 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这样的类型::对于移动很便宜的向量,你可能永远不会注意到(如果你注意到你可以优化)。对于std:::数组这样的类型,移动代价高昂,或者对于不知道移动是否便宜的模板,您更有可能担心它。

#2


28  

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是多余的,因为您是按值返回一个局部变量,编译器会理解,因为您不再使用该局部变量,所以可以从它移动,而不是复制它。

#3


15  

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 not the identifier, it will not be moved automatically, so for example, you would need the explicit std::move in this case:

在返回值上,如果返回表达式直接引用本地lvalue(即此时的xvalue)的名称,那么就不需要std::move。另一方面,如果返回表达式不是标识符,则不会自动移动,例如,您需要显式的std::在本例中移动:

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可能会影响其他优化。

#4


10  

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:

当你有一个函数接受一个rvalue引用时,修改它,然后返回它的一个拷贝。现在,在实践中,这个设计几乎总是更好的:

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.

它还允许您使用非rvalue参数。

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 moves (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).

基本上,如果在要通过移动返回的函数中有一个rvalue引用,则必须调用std::move。如果您有一个局部变量(无论它是否是参数),那么返回它将隐式移动(隐式移动可以省略,而显式移动不能)。如果您有一个函数或操作,它接受局部变量,并返回对该局部变量的引用,那么您需要std::move才能让move发生(例如,三元组?:操作符)。

#5


0  

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
  • 如果已知foo是在其生命周期的末尾,并且。
  • 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.
  • 除了c++规范允许的语义效果之外,std::move的隐式使用不会对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++编译器的优化能力,它是否能够计算出f(foo)的哪些转换;foo。~ foo();f(std::移动(foo));foo。~ foo();在性能或内存消耗方面是有利可图的,同时遵守c++规范规则。


Conceptually speaking, year-2017 C++ compilers, such as GCC 6.3.0, are able to optimize this code:

从概念上讲,year-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 optimize these 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年的程序员需要明确地指定这种优化。

#6


-5  

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.

当从函数返回时,移动是完全没有必要的,并且真正进入了你——程序员——的领域,试图照顾你应该留给编译器的东西。

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:::从函数中移出不属于该函数的变量时,会发生什么?你可以这样说,你永远不会写这样的代码,但是如果你写的代码很好,然后重构它,心不在焉地不改变std::move。你会发现追踪那个bug很有趣。

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 not necessarily create an rvalue or use move semantics.

同样:需要注意的是,从函数返回局部变量并不一定要创建rvalue或使用move语义。

See here.

在这里看到的。

#1


95  

In the case of return std::move(foo); the move is superfluous because of 12.8/32:

如果返回std::move(foo);这一举动是多余的,因为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 first 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.

返回foo;是NRVO的病例,所以拷贝省略是允许的。foo是一个左值。因此,为foo中的“copy”选择的构造函数到meh的返回值,如果存在,则必须是move构造函数。

Adding move does have a potential effect, though: it prevents the move being elided, because return std::move(foo); is not eligible for NRVO.

添加move确实有一个潜在的影响:它阻止移动被省略,因为返回std::move(foo);不符合NRVO要求。

As far as I know, 12.8/32 lays out the only conditions 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列出了唯一的条件,在此条件下,lvalue的副本可以被移动取代。一般来说,编译器不允许在复制之后检测lvalue是否未使用(例如使用DFA),并主动进行更改。我假设这两者之间有一个可观察的区别——如果可观察的行为是相同的那么“假设”规则就适用。

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:::当您希望它移动时,移动一个返回值,并且无论如何它不会移动。那就是:

  • you want it to be moved, and
  • 你希望它被移动
  • it is an lvalue, and
  • 它是一个lvalue,并且
  • it is not eligible for copy elision, and
  • 它不符合复制elision,和。
  • it is not the name of a by-value function parameter.
  • 它不是按值函数参数的名称。

Considering that this is quite fiddly and moves are usually cheap, you might like to say that in non-template code you can simplify this a bit. Use std::move when:

考虑到这相当复杂,而且移动通常很便宜,您可能想说,在非模板代码中,您可以稍微简化一下。使用std::移动时:

  • you want it to be moved, and
  • 你希望它被移动
  • it is an lvalue, and
  • 它是一个lvalue,并且
  • 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这样的类型::对于移动很便宜的向量,你可能永远不会注意到(如果你注意到你可以优化)。对于std:::数组这样的类型,移动代价高昂,或者对于不知道移动是否便宜的模板,您更有可能担心它。

#2


28  

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是多余的,因为您是按值返回一个局部变量,编译器会理解,因为您不再使用该局部变量,所以可以从它移动,而不是复制它。

#3


15  

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 not the identifier, it will not be moved automatically, so for example, you would need the explicit std::move in this case:

在返回值上,如果返回表达式直接引用本地lvalue(即此时的xvalue)的名称,那么就不需要std::move。另一方面,如果返回表达式不是标识符,则不会自动移动,例如,您需要显式的std::在本例中移动:

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可能会影响其他优化。

#4


10  

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:

当你有一个函数接受一个rvalue引用时,修改它,然后返回它的一个拷贝。现在,在实践中,这个设计几乎总是更好的:

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.

它还允许您使用非rvalue参数。

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 moves (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).

基本上,如果在要通过移动返回的函数中有一个rvalue引用,则必须调用std::move。如果您有一个局部变量(无论它是否是参数),那么返回它将隐式移动(隐式移动可以省略,而显式移动不能)。如果您有一个函数或操作,它接受局部变量,并返回对该局部变量的引用,那么您需要std::move才能让move发生(例如,三元组?:操作符)。

#5


0  

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
  • 如果已知foo是在其生命周期的末尾,并且。
  • 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.
  • 除了c++规范允许的语义效果之外,std::move的隐式使用不会对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++编译器的优化能力,它是否能够计算出f(foo)的哪些转换;foo。~ foo();f(std::移动(foo));foo。~ foo();在性能或内存消耗方面是有利可图的,同时遵守c++规范规则。


Conceptually speaking, year-2017 C++ compilers, such as GCC 6.3.0, are able to optimize this code:

从概念上讲,year-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 optimize these 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年的程序员需要明确地指定这种优化。

#6


-5  

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.

当从函数返回时,移动是完全没有必要的,并且真正进入了你——程序员——的领域,试图照顾你应该留给编译器的东西。

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:::从函数中移出不属于该函数的变量时,会发生什么?你可以这样说,你永远不会写这样的代码,但是如果你写的代码很好,然后重构它,心不在焉地不改变std::move。你会发现追踪那个bug很有趣。

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 not necessarily create an rvalue or use move semantics.

同样:需要注意的是,从函数返回局部变量并不一定要创建rvalue或使用move语义。

See here.

在这里看到的。