An rvalue expression is any expression which can be implicitly moved from, regardless of whether it has identity.
More precisely, rvalue expressions may be used as the argument to a function that takes a parameter of type
T && (where
T is the type of
expr). Only rvalue expressions may be given as arguments to such function parameters; if a non-rvalue expression is used, then overload resolution will pick any function that does not use an rvalue reference parameter. And if none exist, then you get an error.
The category of rvalue expressions includes all xvalue and prvalue expressions, and only those expressions.
The standard library function
std::move exists to explicitly transform a non-rvalue expression into an rvalue. More specifically, it turns the expression into an xvalue, since even if it was an identity-less prvalue expression before, by passing it as a parameter to
std::move, it gains identity (the function's parameter name) and becomes an xvalue.
Consider the following:
std::string str("init"); //1 std::string test1(str); //2 std::string test2(std::move(str)); //3 str = std::string("new value"); //4 std::string &&str_ref = std::move(str); //5 std::string test3(str_ref); //6
std::string has a constructor which takes a single parameter of type
std::string&&, commonly called a "move constructor". However, the value category of the expression
str is not an rvalue (specifically it is an lvalue), so it cannot call that constructor overload. Instead, it calls the
const std::string& overload, the copy constructor.
Line 3 changes things. The return value of
std::move is a
T is the base type of the parameter passed in. So
std::string&&. A function call who's return value is an rvalue reference is an rvalue expression (specifically an xvalue), so it may call the move constructor of
std::string. After line 3,
str has been moved from (who's contents are now undefined).
Line 4 passes a temporary to the assignment operator of
std::string. This has an overload which takes a
std::string&&. The expression
std::string("new value") is an rvalue expression (specifically a prvalue), so it may call that overload. Thus, the temporary is moved into
str, replacing the undefined contents with specific contents.
Line 5 creates a named rvalue reference called
str_ref that refers to
str. This is where value categories get confusing.
str_ref is an rvalue reference to
std::string, the value category of the expression
str_ref is not an rvalue. It is an lvalue expression. Yes, really. Because of this, one cannot call the move constructor of
std::string with the expression
str_ref. Line 6 therefore copies the value of
To move it, we would have to employ