Your first bullet is incorrect fundamentally, arg
is not a lvalue, neither it is an rvalue. Neither it's a rvalue or lvalue reference, because std::move
is a template. In template context a function argument of type T&&
is a forwarding reference, if T
is a template-parameter. Forwarding reference becomes of type which appropriate, depending on what is T.
(and implicit conversion from lvalue to rvalue reference is forbidden by standard).
A cast is required literally because of that. Following code is incorrect, because you can't call foo(v)
, as v
is a named object and it can be an lvalue:
void foo(int && a) { a = 5; }
int main()
{
int v;
foo(v);
std::cout << a << std::endl;
}
But if foo()
is a template, it may become a function with int&
, const int&
and int&&
arguments.
template<class T>
void foo(T && a) { a = 5; }
You would be able to call foo(v+5)
, where argument is a temporary, which can be bound to rvalue reference. foo
will change the temporary object which stops to exist after function call. That's the exact action which move constructors usually have to do - to modify temporary object before its destructor is called.
NB: An rvalue argument would cease to exist earlier , either after its use or at end of function call.
Forwarding references are a special kind of reference syntax designed to preserve the value category of a function argument. I.e. non-template function
Object&& move(Object&& arg)
is not equal to std::move
for Object
, which declared something like (c++11):
template<class T>
std::remove_reference<T>::type&& move( T&& t );
In non-template function arg
is an lvalue, in template it have same value category as expression used to initialize it. In template std::remove_reference<T>::type
refers to T, so std::remove_reference<T>::type&&
is a true rvalue reference to T - a way around T&&
alternative meaning.
By analogy to function call above, if implicit conversion was possible, then it would be possible to call move constructor where copy constructor is appropriate but missing, i.e. by mistake. return static_cast<Object&&>(arg);
results in initialization involving call to Object::Object(Object&&)
by definition of return
, return arg
would call Object::Object(const Object&)
.
Template std::move
is type-correct "wrapper" around the static_cast
to facilitate "implicit" cast, to simplify code by removing repeated static_cast
with explicit type from code.