The implementation of std::move
basically looks like this:
template<typename T>
typename std::remove_reference<T>::type&&
move(T&& t)
{
return static_cast<typename std::remove_reference<T>::type&&>(t);
}
Note that the parameter of std::move
is a universal reference (also known as a forwarding reference, but we're not forwarding here). That is, you can std::move
both lvalues and rvalues:
std::string a, b, c;
// ...
foo(std::move(a)); // fine, a is an lvalue
foo(std::move(b + c)); // nonsense, b + c is already an rvalue
But since the whole point of std::move
is to cast to an rvalue, why are we even allowed to std::move
rvalues? Wouldn't it make more sense if std::move
would only accept lvalues?
template<typename T>
T&&
move(T& t)
{
return static_cast<T&&>(t);
}
Then the nonsensical expression std::move(b + c)
would cause a compile-time error.
The above implementation of std::move
would also be much easier to understand for beginners, because the code does exactly what it appears to do: It takes an lvalue and returns an rvalue. You don't have to understand universal references, reference collapsing and meta functions.
So why was std::move
designed to take both lvalues and rvalues?