Reputation: 127
I saw some similar questions but could not find a clear explanation for my problem. Here is the code (that can be found here: http://melpon.org/wandbox/permlink/nBiik8pMkpKCD3Jv):
#include <iostream>
class A {
public:
explicit A(int a) {std::cout << "Main constructor" << std::endl;}
A(const A& a) {std::cout << "Copy constructor" << std::endl;}
A& operator =(const A& a) {std::cout << "Copy assignment" << std::endl; return *this;}
A(A&& a) {std::cout << "Move constructor" << std::endl;}
A& operator =(A&& a) {std::cout << "Move assignemnt" << std::endl; return *this;}
};
A getA(bool b) {std::cout << "In getA" << std::endl; A a(0); return b ? a : A(1);}
A getA_Move(bool b) {std::cout << "In getA_Move" << std::endl; A a(0); return std::move(b ? a : A(1));}
int main(void) {
std::cout << "\nA a0(getA(true))" << std::endl;
A a0(getA(true));
std::cout << "\nA a1(getA(false))" << std::endl;
A a1(getA(false));
std::cout << "\nA a2(getA(true))" << std::endl;
A a2(getA_Move(true));
std::cout << "\nA a3(getA(false))" << std::endl;
A a3(getA_Move(false));
}
As far as I understood: The return of a function is an rvalue (because as opposite to an lvalue it has no name and thus cannot be re used). Thus when creating an object which is taking the return value of a function as a parameter this parameter should be moved if it is moveable. However this is not what I can observe:
Start
A a0(getA(true))
Main constructor
In getA
Copy constructor <- Why is not this a move ?
A a1(getA(false))
Main constructor
In getA
Main constructor
A a2(getA(true))
Main constructor
In getA_Move
Copy constructor <- I would have expected a move here as well
Move constructor
A a3(getA(false))
Main constructor
In getA_Move
Main constructor
Move constructor
0
Finish
Thanks @T.C. I modified the program to use a normal if and then the program works as expected. Result can be found here: http://melpon.org/wandbox/permlink/6h0ODi1SHdUj4HvX
Upvotes: 2
Views: 690
Reputation: 137315
Briefly, this is because foo ? lvalue-of-X : rvalue-of-X
results in a temporary of type X
initialized from the selected operand - a copy if the second is selected, and a move if the third is selected.
Then, that temporary is returned - first moved into the return value, then into the variable in main
.
The "local variables are treated as rvalues in return" rule applies only when said local variable is being returned directly.
GCC's handling here is a little bugged - it elides the move from the third operand even with -fno-elide-constructors
. With clang, you'll see the triple move when the third operand is picked.
Upvotes: 6
Reputation: 2462
this is due to copy elision compiler optimization. "Copy elision is the only allowed form of optimization that can change the observable side-effects"
Upvotes: 0