The expression f()
returns by value, so it's a prvalue.
this creates a new object of type X
initialized with the expression f()
which is a prvalue of type X
. How the new X
object will be constructed depends on whether it has a move constructor or not. If it has a move constructor (or a template constructor that accepts X
rvalues) it will be called, otherwise if it has a copy constructor that will be called. In practice the compiler will almost certainly elide the constructor call, but the appropriate constructor must be accessible and not deleted.
This doesn't even compile, you get a downvote for not trying it before posting!
This create a new reference of type X&&
and initializes it by binding it to the prvalue returned by f()
. That prvalue will have its lifetime extended to the same lifetime as the reference x
.
The difference in behaviour is probably nothing, assuming the move/copy is elided, but there is a difference in semantics between (1) and (3) because one does overload resolution for a constructor, which could fail, and the other always works.
What if g returned an X: is the following legal and correct?
It's legal. The move
is unnecessary, there's a special rule that says when returning a local variable by value the constructor lookup is first done as though the variable was an rvalue so if X
has a move constructor it will be used, whether or not you use move(x)
. RVO should "chain". If g
returned X&
you'd have a problem in both cases, because the object it would bind to would go out of scope at the end of g
.
(N.B. It's good practice to always qualify std::move
to prevent ADL. Similarly for std::forward
. If you mean to call std::move
or std::forward
then be explicit, don't rely on there being no overloads in scope or visible, move
and forward
are not customization points like swap
.)
Instead of learning C++ by asking questions on SO, why not write code to test what happens and prove it to yourself? With G++ you can use the -fno-elide-constructors
flag to turn off constructor elision to see what would happen in the absence of elision, and when not using that flag (the default) you can easily test whether RVO "chains" for yourself.