This very simple code is allowed:
const s& tt = std::move(s()); // Also valid without 'const'
Rvalue references are implicitly converted to rvalues (more specifically, to xvalues) as one of the standard conversions (chapter 4 of the C++ standard):
The effect of any implicit conversion is the same as performing the corresponding declaration and initialization and then using the temporary variable as the result of the conversion. The result is an lvalue if T is an lvalue reference type or an rvalue reference to function type (8.3.2), an xvalue if T is an rvalue reference to object type, and a prvalue otherwise
Rvalues (including xvalues) can be bound to
const lvalue references so that you can pass a temporary to a function with such a parameter:
void foo(const bar &a); // ... foo(bar());
(A temporary is an rvalue; in this case, the result of
bar() is an rvalue). There is no reason not to allow this, since the temporary always lives as long as the containing expression - in this case, the function call - and so it will not create a dangling reference inside
This means it is always possible to adjust the signature of a function
fun(const bar &) - and change the implementation accordingly of course! - since temporary arguments will still be accepted, and the semantics should be the same from the perspective of the caller;
const implies the object won't be modified, which is also the case if it passed by copy.
const references are not allowed; one practical reason is because they imply that the value should be modified in some meaningful way, and if the value is a temporary, this would be lost. However, you can convert an rvalue to an lvalue if you really want to do so, with some caveats, as described in this answer to another question.
Allowing an rvalue to bind to a
const lvalue reference, other than allowing temporary arguments to be passed by reference, is also good for cases where the exact parameter type is not known but you want to allow move semantics if it is possible. Suppose that I am calling a function defined as
foo2(const bar &) or
foo2(bar) and which may or may not have an overload
foo2(bar &&), and I want move semantics to be used if possible; I can safely use
std::move to create an rvalue since it will apply in either case. This example might seem a little contrived, but it is the sort of thing that can come up quite a bit when writing templates. In code:
bar bb = bar(); foo2(std::move(bb)); // above is legal if foo2 is declared as: // foo2(bar) // foo2(const bar &) // and calls overload foo2(bar &&) if it is defined.
In the case of other rvalue-to-lvalue-reference assignments involving a temporary, the lifetime of the temporary is extended to that of the reference, so that dangling references are not created even in contexts other than parameter passing:
const bar &b = bar(); // temporary lifetime is extended
In the above, the
bar object will not be destroyed until the reference
b goes out of scope.