First, examine the sub-expression play(5)
. This expression is the same in both cases.
In a function call expression each parameter is copy-initialized from its argument (ISO/IEC 14882:2003 5.2.2/4). In this case this involves converting 5
to a B
by using the non-explicit constructor taking an int
to create a temporary B
and then using the copy-constructor to initialize the parameter b
. However, the implementation is permitted to eliminate the temporary by directly initializing b
using the converting constructor from int
under the rules specified in 12.8.
The type of play(5)
is B
and - as function returning a non-reference - it is an rvalue.
The return
statement implicitly converts the return expression to the type of the return value (6.6.3) and then copy-initializes (8.5/12) the return object with the converted expression.
In this case the return expression is already of the correct type, so no conversion is required but the copy initialization is still required.
Aside on return value optimizations
The named return value optimization (NRVO) refers to the situation where the return statement is if the form return x;
where x
is an automatic object local to the function. When occurs the implementation is allowed to construct x
in the location for the return value and eliminate the copy-initialization at the point of return
.
Although it is not named as such in the standard, NRVO usually refers to the first situation described in 12.8/15.
This particular optimization is not possible in play
because b
is not an object local to the function body, it is the name of the parameter which has already been constructed by the time the function is entered.
The (unnamed) return value optimization (RVO) has even less agreement on what it refers to but is usually used to refer to the situation where the return expression is not a named object but an expression where the conversion to the return type and copy-initialization of the return object can be combined so that the return object is initialized straight from the result of the conversion eliminating one temporary object.
The RVO doesn't apply in play
because b
is already of type B
so the copy-initialization is equivalent to direct-initialization and no temporary object is necessary.
In both cases play(5)
requires the construction of a B
using B(int)
for the parameter and a copy-initialization of B
to the return object. It may also use a second copy in the initialization of the parameter but many compilers eliminate this copy even when optimizations are not explicitly requested. Both (or all) of these objects are temporaries.
In the expression statement t1 = play(5);
the copy assignment operator will be called to copy the value of the return value of play
to t1
and the two temporaries (parameter and return value of play
) will be destroyed. Naturally t1
must have been constructed prior to this statement and its destructor will be called at the end of its lifetime.
In the declaration statement B t1 = play(5);
, logically t1
is initialized with the return value of play and exactly the same number of temporaries will be used as the expression statement t1 = play(5);
. However, this is the second of the situations covered in 12.8/15 where the implementation is allowed to eliminate the temporary used for the return value of play
and instead allow the return object to alias t1
. The play
function operates in exactly the same way but because it the return object is just an alias to t1
its return statement effectively directly initializes t1
and there is no separate temporary object for the return value that needs to be destroyed.