Reputation: 1864
There are quite many threads about rvalue (reference).
But I haven't found the answer to my following question:
According to http://en.cppreference.com/w/cpp/utility/move, std::move() takes a rvalue reference as the parameter.
According to the graph in C++11 Standard https://i.sstatic.net/YKlod.png, a lvalue won't be rvalue.
Common usage of std::move():
Foo f;
std::deque<Foo> fq;
fq.push_back(std::move(f));
f is a lvalue because it is an object according to C++11 standard:
An lvalue (so-called, historically, because lvalues could appear on the left-hand side of an assignment expression) designates a function or an object. [Example: If E is an expression of pointer type, then *E is an lvalue expression referring to the object or function to which E points. As another example, the result of calling a function whose return type is an lvalue reference is an lvalue.]
Then, in theory, std::move(f) should give me an compile error, but it doesn't. I am sure that I miss something...
Any input is welcome.
Upvotes: 0
Views: 156
Reputation: 137770
The parameter to std::move
is type T &&
, where T
is deduced. This is called a forwarding reference.*
The trick is that a declaration T & &&
generated by a template is equivalent to T &
. When you pass an lvalue int
to move
, then the type T
in T &&
is deduced T = int &
and the entire parameter is type int & && = int &
. This is called reference collapsing.
As you observed, it is illegal to pass an lvalue to an rvalue parameter, but reference collapsing prevents this from happening.
The practice of defining a function which works with lvalue or rvalue references, by declaring such a reference, is called perfect forwarding, hence the reference is a forwading reference. The argument is "forwarded" because it behaves the same inside the function as it did in the function call, provided std::forward
is used to restore rvalue-ness.
* The term universal reference is being deprecated.
Upvotes: 3
Reputation: 385098
"Rvalue reference" doesn't mean a reference that is an rvalue. It's just a description of the type (T&&
rather than T&
). It's confusingly named in that regard. The value category of f
is indeed lvalue and that's fine.
Upvotes: 0
Reputation:
As mentioned by @Deduplicator, &&
only means universal reference in cases where T
is a deduced type. Scott Meyer's comprehensive blog post on Universal References provides an easy to remember quote:
If a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is a universal reference.
As well as a code sample:
Widget&& var1 = someWidget; // here, “&&” means rvalue reference
auto&& var2 = var1; // here, “&&” does not mean rvalue reference
template<typename T>
void f(std::vector<T>&& param); // here, “&&” means rvalue reference
template<typename T>
void f(T&& param); // here, “&&”does not mean rvalue reference
As covered by Thomas Becker, std::move
accepts a universal reference to take advantage of perfect forwarding.
Even though push_back takes a T&&
, this is not a universal reference, as explained by Scott Meyers. T
is already deduced in this instance:
template <class T>
void vector<T>::push_back(T&& x);
so the parameter actually becomes an rvalue reference. To give a practical example, consider a template class that doesn't have an overload that takes an lvalue reference:
template <typename T>
struct Test
{
void push_back(T&& t);
};
template <typename T>
void Test<T>::push_back(T&& t) { std::cout << "rvalue overload.\n"; }
int main()
{
Test<int> t;
t.push_back(42);
int i = 50;
t.push_back(i);
return 0;
}
t.push_back(i)
would give an error.
Upvotes: 2