Can returning a local variable by value in C++11/14 result in the return value being constructed by rvalue when no copy/move is involved?

The rule for this situation changed between 2011 and 2014. The compiler should now treat localB as an rvalue.

The applicable rule for return statements is found in §12.8 [class.copy]/p32, which reads in C++14 (quoting N3936, 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. If the first overload resolution fails or was not performed,
or if the type of the first parameter of the selected constructor is
not an rvalue reference to the object’s type (possibly cv-qualified),
overload resolution is performed again, considering the object as an
lvalue.

The bolded clause was added by CWG issue 1579, expressly to require the converting move constructor A::A(B&&) to be called here. This is implemented in GCC 5 and Clang 3.9.

Back in 2011, this “try rvalue first” rule was closely tied to the criteria for copy elision (quoting N3337):

When the criteria for elision of a copy operation are met or would be
met save for the fact that the source object is a function parameter,
and the object to be copied is designated by an lvalue, overload
resolution to select the constructor for the copy is first performed
as if the object were designated by an rvalue.

Since copy elision necessarily requires the two to have the same type, this paragraph didn’t apply, and the compiler had to use the A::A(B&) constructor.

Note that as CWG 1579 is considered a DR against C++11, compilers should implement its resolution even in C++11 mode.

Leave a Comment