lval/rval
. In concept (though not always in practice), rvalues correspond to temporary objects returned from functions, while lvalues correspond to objects you can refer to, either by name or by following a pointer or lvalue reference.
A useful heuristic to determine whether an expression is an lvalue is to ask if you can take its address.
Here, itād be perfectly valid to take rhsās address inside Widgetās move constructor, so rhs is an lvalue, even though its type is an rvalue reference. (By similar reasoning, all parameters are lvalues.)
class Annotation {
public:
explicit Annotation(const std::string text)
: value(std::move(text)) // "move" text into value; this code
{ ā¦ } // doesn't do what it seems to!
ā¦
private:
std::string value;
};
class string { // std::string is actually a
public: // typedef for std::basic_string<char>
ā¦
string(const string& rhs); // copy ctor
string(string&& rhs); // move ctor
ā¦
};
std::move(text) : cast left val const std::string -> right val const std::string
rvalue of type const std::string
the move ctor takes right value reference to non-cast string[1] the copy ctor takes left value reference to const string[2]
const rvalue -x-> [1] const rvalue ā> [2]
Moving a value out of an object generally modifies the object, so the language should not permit const objects to be passed to functions (such as move constructors) that could modify them.
There are two lessons to be drawn from this example. First, donāt declare objects const if you want to be able to move from them. Move requests on const objects are silently transformed into copy operations. Second, std::move not only doesnāt actually move anything, it doesnāt even guarantee that the object itās casting will be eligible to be moved. The only thing you know for sure about the result of applying std::move to an object is that itās an rvalue.