#include <iostream>
#include <time.h>
class A
{
public:
A() { std::cout << "a ctor\n"; }
A(const A&) { std::cout << "a copy ctor\n"; }
A(A&&) { std::cout << "a move ctor\n"; }
};
A f(int i)
{
A a1;
return i != 0 ? a1 : A{};
}
int main()
{
srand(time(0));
f(rand());
return 0;
}
The output is:
a ctor
a copy ctor
I would expect that a1 in f() will be moved not copied. If I change f() just a little bit, it's not a copy anymore but a move:
A f(int i)
{
A a1;
if (i != 0)
{
return a1;
}
return A{};
}
The output is:
a ctor
a move ctor
Could you explain me how does this work? GCC 9.3.0
(edit: added random number to prevent RVO)
The difference you're seeing is due to the use of the ternary/conditional operator. The ternary operator determines a common type and value category for its second and third operands, and that is determined at compile time. See here.
In your case:
return i != 0 ? a1 : A{};
the common type is A
and the common value category is prvalue since A{}
is an prvalue. However, a1
is an lvalue and a prvalue temporary copy of it will have to be made in the lvalue-to-rvalue conversion. This explains why you see the copy constructor invoked when the condition is true: a copy of a1
is made to convert it to an prvalue. The prvalue is copy elided by your compiler.
In the second example, where you have an if
statement, these rules don't apply as in the case of the ternary operator. So no copy constructor invoked here.
To address your comment about a conditional statement with lvalue for the second and third operands, according to the rules of copy elision it is allowed if:
In a return statement, when the operand is the name of a non-volatile object with automatic storage duration, which isn't a function parameter or a catch clause parameter, and which is of the same class type (ignoring cv-qualification) as the function return type. This variant of copy elision is known as NRVO, "named return value optimization".
A conditional statement like
return i != 0 ? a1 : a1;
where the second and third operands are lvalues, does not fulfill this criteria. The expression is a conditional, not the name of an automatic object. Hence no copy elision and the copy constructor is invoked.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With