In the code below, why doesn't the first call mkme = mvme_rv
dispatch to T& operator=(const T&&)
?
#include <iostream>
#include <string>
#include <vector>
using namespace std;
using T = vector<int>;
int main()
{
T mvme(10, 1), mkme;
T&& mvme_rv = move(mvme); // rvalue ref?
mkme = mvme_rv; // calls T& operator=(const T&)?
cout << mvme.empty(); // 0
mkme = move(mvme_rv); // calls T& operator=(const T&&)?
cout << mvme.empty(); // 1
}
This line of code:
is a copy and will thus use a copy assignment (
T& operator=(const T&)
). The key thing about this is BOTH objects can be used afterwards and should - if implemented correctly - provide two identical objects.By contrast, this line of code:
is a move assignment (
T& operator=(const T&&)
). By convention, this will trash themvme_rv
object (or at least clear it) and makemkme
basically whatmvme_rv
was previously.Effectively
T&&
means a temporary object (aka xvalue) - something that will not last. Thestd::move
method basically casts the object to a temporary (credit to @richard-Hodges for that wording). This can then be used in the move assignment method.So finally to answer you question of why doesn't
mkme = mvme_rv
dispatch toT& operator=(const T&&)
: it's becausemvme_rv
isn't a temporary object (aka xavalue).More about xvalues: http://en.cppreference.com/w/cpp/language/value_category
As skypjack correctly comments, accessing an object through its name always results in an lvalue reference.
This is a safety feature and if you think it through you will realise that you are glad of it.
As you know,
std::move
simply casts an l-value reference to an r-value reference. If we use the returned r-value reference immediately (i.e. un-named) then it remains an r-value reference.This means that the use of the r-value can only be at the point in the code where
move(x)
is mentioned. From a code-reader's perspective, it's now easy to see where x's state became undefined.so:
does not work. If it did, someone maintaining the code would have a hard time seeing (and remembering) that x (defined on line 1) enters an undefined state on line 55 through a reference taken on line 2.
This is a good deal more explicit: