There are some articles to explain #1, but I don't understand why #2
also fails.
Remember, if it has a name, it's an lvalue.
In your second case, x is still an lvalue even if it's an rvalue reference. std::move
has the ability to turn lvalues into rvalues by doing nothing more than a static_cast<Type&&>(yourVar)
on them. The resulting expression is an rvalue that can be accepted by any code requesting an rvalue reference. (a Type &&
)
I'll illustrate with a few examples. In your original example, replace:
std::string str("xxx");
test1(str);
with
test1(string("xxx"));
There, the string object no longer has a name, it is now an rvalue and is accepted by test1.
How does move work? Again, quite simple. Again, replace your call with:
test1(std::move(str));
or with
test1(static_cast<std::string&&>(str));
Same thing basically, just that std::move better explains the intention.
Takeaways
if it has a name it's an lvalue
if it doesn't have a name, it's an rvalue and can be accepted wherever a Type &&
(an rvalue reference) is asked for.
You can static_cast
an lvalue to an rvalue (but use std::move
instead, that's what it's for), but you need to know what you're doing when you're doing that. (that is, use it on types that have move semantics implemented, I won't go into any detail here, I'll just link to a great article on the move semantics topic)