Why copy elision without working with std :: move?

I use the following code to test a copy:

class foo { public: foo() {cout<<"ctor"<<endl;}; foo(const foo &rhs) {cout<<"copy ctor"<<endl;} }; int g(foo a) { return 0; } int main() { foo a; g(std::move(a)); return 0; } 

I expected that only the default constructor would be called, because the g() argument is rvalue, and the copy will be deleted. But the result shows that both the default constructor and the copy constructor are called. Why?

And if I change the function call to g(foo()) , the copy will be deleted. What is the difference between return types foo() and std::move(a) ? How can I make a copy of the compiler instance on lvalue?

+6
source share
2 answers

Copying elision for can occur only in a few specific situations, the most common of which is copying temporary (the rest return local residents and throw / catch exceptions). Your code is not generated temporarily, so the copy is not deleted.

The copy constructor is called because foo does not have a move constructor (move constructors are implicitly generated for classes with explicit copy constructors), so std::move(a) matches the constructor foo(const foo &rhs) (which is used to build the function argument).

Copying the lvalue value can be undone in the following situations (although there is no way to force the compiler to throw an exception):

 foo fn() { foo localAutomaticVariable; return localAutomaticVariable; //Copy to construct return value may be elided } int main() { try { foo localVariable; throw localVariable; //The copy to construct the exception may be elided } catch(...) {} } 

If you want to avoid copying when passing arguments to a function, you can use a move constructor that imposes object resources on it:

 class bar { public: bar() {cout<<"ctor"<<endl;}; bar(const bar &rhs) {cout<<"copy ctor"<<endl;} bar(bar &&rhs) {cout<<"move ctor"<<endl;} }; void fn(bar a) { } //Prints: //"ctor" //"move ctor" int main() { bar b; f(std::move(b)); } 

In addition, whenever copy permission is allowed but does not occur, the move constructor will be used if it is available.

+6
source

You need to declare g as:

 int g(foo && a) //accept argument as rvalue reference { return 0; } 

Now it can take an argument with rvalue-reference.

In your case, although the expression std::move(a) creates an rvalue, it does not bind to a parameter that takes an argument by value. The host should also be an rvalue-reference.

In the case of g(foo()) copying is done by the compiler, which is an optimization. This is NOT a language requirement [before C ++ 17] . You can turn off this optimization if you want: then g(foo()) and g(std::move(a)) will behave exactly as expected.

But if you change g , as I suggested above, calling g(foo()) will not make a copy, because it is a language requirement not to copy with && . This is no longer compiler optimization.

+4
source

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


All Articles