0
votes

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 http://i.stack.imgur.com/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.

3
Actually, the fuss with std::move is redundant. std::vector has emplace_back for this purposeArmen Tsirunyan
Actually, std::move does not take an rvalue-reference, but a forwarding-reference (aka universal reference), which means it can be either an lvalue and and rvalue reference.Deduplicator
@remyabel what's the "collapsing rule"?Hei
@Deduplicator so T&& is forwarding-reference/universal reference? Then, how do we specify rvalue reference as a type of a parameter?Hei
@Hei It is only in a context where T is deduced, like the prototype of the std::move-template. In that case, if you really want to restrict to rvalue-references, use SFINAE or static_assert and type-traits.Deduplicator

3 Answers

3
votes

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.

2
votes

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.

0
votes

"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.