After reading this question. I created this small little test:
class A{
public:
A(){}
A(const A&){printf("copy\n");}
A(A&&){printf("move\n");}
static A f(){
A a;
return a;}
static A g(){
A a;
return (a);}//could be return *&a; too.
static A h(){
A a;
return true?a:a;}
};
The result is (without RVO and NRVO):
As far as I know the rules used to decide whether to use copy or move are described in 12.8.32:
Which refers to the rules of 12.8.31: (I only show the relevant part)
Following these rules I understand what happens for f and h:
What about g?
To me it looks really like h. I am returning an expression which is not the name of an automatic object and as such I thought it would be copied however it is moved. What is going on here?
In most cases there is no difference in writing a
or (a)
. The relevant part of the spec is §5.1p6 (emphasis mine):
A parenthesized expression is a primary expression whose type and value are identical to those of the enclosed expression. The presence of parentheses does not affect whether the expression is an lvalue. The parenthesized expression can be used in exactly the same contexts as those where the enclosed expression can be used, and with the same meaning, except as otherwise indicated.
Hence, the same reasoning applies to the return value of your function g
as you have given for f
.
In the upcomming standard C++14 this has been clarified §12.8p32 (emphasis mine):
When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.
For those who want to know, when the parentheses matter, here is an example:
namespace N {
struct S { };
void f(S);
}
void g() {
N::S s;
f(s); // OK: calls N::f
(f)(s); // error: N::f not considered; parentheses
// prevent argument-dependent lookup
}
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