C++03 and before
Obj
is copied twice. Once by the return
statement (constructing the return value), and once to initialize o1
by copying the return value.
C++11 and C++14
If Obj
has a usable move constructor, it is moved twice and copied zero times. The return
statement must use a move, even though the returned expression is an lvalue. This "move optimization" is mandatory due to a special rule in the language; o2
must not be copied, even when optimizations are disabled. A second move occurs when o1
is initialized.
If Obj
has either no move constructor or an implicitly deleted move constructor, the copy constructor is used twice.
If Obj
has an explicitly deleted move constructor, the program is ill-formed since the initialization of o1
tries to use the deleted move constructor.
C++17 and later
If Obj
has a usable move constructor, it is moved once, when the return
statement is executed. As mentioned above, it is mandatory for the compiler to use a move instead of a copy. The construction of o1
involves neither a copy nor a move. Rather, the return
statement in f()
initializes o1
, without the involvement of a temporary. This is because of "guaranteed copy elision": the language requires the copy to be elided, even if optimizations are disabled. This is because f()
is a prvalue, and a prvalue is not materialized (i.e., instantiated as a temporary object) unless it is necessary to do so. The "legal fiction" created by the standard is that f()
actually returns a "recipe" for creating Obj
, not an Obj
itself. In practice, this can be implemented the same way that the (optional) return value optimization was implemented in earlier versions of the standard: the caller passes a pointer to o1
directly into f
, and the return
statement constructs Obj
into this pointer.
If the move constructor of Obj
is implicitly deleted or does not exist, the copy constructor will be used by the return
statement, so there will be one copy and zero moves.
If the move constructor of Obj
is explicitly deleted, the program is ill-formed as in the C++11/C++14 case.
In all cases
The copies/moves in the above situations can be optimized out. In cases that involve more than one copy/move operation, the compiler can optimize out any or all of them.
Obj
? – IvanaivanahObj
has a move constructor. – IvanaivanahObj
has a move constructor, edit this detail into the question, or just show the definition ofObj
. The comments section is too short to give an answer. – IvanaivanahObj
. If you say "maybe it does have a move constructor, or maybe it doesn't", then it doubles the amount of work everyone has to do to answer your question, by addressing all cases. It's better to ask one question about a specific situation, then ask another question if there's another situation you want to know about as well. – Ivanaivanah