Rvalue reference arguments allow the efficient transfer of the ownership of objects. Therefore, it is expected that rvalue arguments or their
subobjects are, conditionally or not, moved into their destination variables.
The ownership is unclear when an rvalue argument, including its subobject or elements, is never moved. This might lead to bugs and performance
issues.
This rule does not apply when the argument is a forwarding reference.
Exceptions
For the C++23 or later standard, this rule does not raise issues if the function returns the rvalue reference parameter. In such cases, the
parameter is implicitly moved, and an explicit call to std::move
is not required:
Shape updateShape(Shape&& shape) {
/* ... */
return shape; // Compliant: implicitly moves shape
}
When returning a parameter or variable of rvalue reference type, an implicit move was introduced in C++20 and retroactively applied to previous
standards. As a consequence, the behavior of such return statements is not consistent across compilers and standard versions.
Furthermore, with the C++20 rules, the implicit move is not triggered if the function returns a reference:
Shape&& updateShape(Shape&& shape) {
/* ... */
// C++23: Implicit move, equivalent to `std::move(shape)`
// C++20: No move and ill-formed as Shape&& reference cannot bound to Shape&
return shape;
}
Due to all of the above, this rule does not treat return p
as an exception in C++ standard before C++23, and requires the explicit
move return std::move(p)
.
In contrast to returning local (stack) variables, named return value optimization (NRVO) does not apply to function parameters, so an explicit
std::move
call has no impact on optimizations.