Why is using the move constructor in the returned statement legal?

Consider the following:

#include <iostream> #define trace(name) std::cout << #name << " (" << this << "), i = " << i << std::endl class C { C(C const&); C& operator=(C const&); public: int i; C() : i(42) { trace(CTOR); } C(C&& other) : i(other.i) { trace(MOVE); other.i = 0; } C& operator=(C&& other) { trace(ASGN); other.i = 0; return *this; } ~C() { trace(DTOR); } }; C func1(bool c) { C local; if (c) return local; else return C(); } int main() { C local(func1(true)); return 0; } 

Both MSC and g ++ enable return local and use the move constructor (as shown in the output). Although this makes a lot of sense to me, and I think it probably should be that way, I can't find the text in the standard that allows it. As far as I can tell, the argument of the move constructor should be either prvalue (which is clearly wrong) or xvalue; this is actually an lvalue value that will make returning as illegal as C other = local; into the body of the function (which does not compile).

+5
source share
2 answers

C ++ 11 12.8 / 32: When the criteria for performing the copy operation are met or are executed, except that the source object is a parameter of the function and the object to be copied is indicated by the value lvalue, overload resolution to select the constructor for the copy is first executed as if the object was designated rvalue .

The return of a local automatic variable meets the elision criteria; treating it as if it were an rvalue, selects the move constructor.

+4
source

When moving semantics added in C ++ to C ++ 11, decisions are made about where to automatically perform the move.

The following general rule was that implicit movement should occur when copying was legal.

Copying elision is legal if you have an anonymous object that you copy to another instance. The compiler can legally exclude a copy and consider two objects as a whole with a lifetime equal to the union of two objects of life.

While copying elision is legal, you are returning a local variable from the function. This was known as NRVO (called return value optimization). This optimization in C ++ 03 allows you to declare a return value and is created directly in the place where the return value is returned. When do you return retval; The copy does not occur.

This cannot be done without encrusting when you return several different objects in different places of your function.

However, when moving semantics when adding, this place was another place where implicit movement can occur. A return local_var; in a function that returns a variable of the same type as local_var can be deleted, and if you cannot do this, you can implicitly go from local_var to the return value.

+6
source

Source: https://habr.com/ru/post/1204125/


All Articles