What's the difference between an ordinary rvalue reference and one returned by std::forward?
Asked Answered
H

2

18

I can't do this:

int &&q = 7;
int &&r = q; 
//Error Message:
//cannot convert from 'int' to 'int &&'
//You cannot bind an lvalue to an rvalue reference

If I understand correctly, when initializing an rvalue reference, there's a temporary variable got initialized too. So int &&q = 7; can be considered as:

int temp = 7;
int &&q = temp;

And when using a reference on the right side, I am actually using the referee. So int &&r = q; can be considered as:

int &&r = temp;  //bind an lvalue to an rvalue reference, cause error, understandable

So above is how I understand the compiler error occurs.


Why adding std::forward can solve that?

int &&q = 7;
int &&r = std::forward<int>(q);

I know the std::forward always returns an rvalue reference, how is the reference returned by std::forward different from int&&q?

Hemidemisemiquaver answered 24/8, 2018 at 6:4 Comment(4)
Equivalent is not the same as identical. Hard coded values are not treated as regular variablesClarindaclarine
Don't think I could explain it better than Scott, so check out this: isocpp.org/blog/2012/11/…Resee
As others have hinted, int &&r = std::move(q); would be the more idiomatic way to do the cast in this case. std::forward is meant for use in "perfect forwarding" to pass on parameters which have template types such as template <typename T> void f(T&& x) or template <typename... T> void f(T&&... x)Brassie
Another possible surprise: decltype(q) is equivalent to int&& while decltype((q)) is equivalent to int&Brassie
M
14

how is the reference returned by std::forward different from int&&q ?

Their value categories are different. And note that types and value categories are different things.

q is a named variable, it's qualified as lvalue, so it can't be bound to rvalue reference.

(emphasis mine)

the name of a variable, a function, a template parameter object (since C++20), or a data member, regardless of type, such as std::cin or std::endl. Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression;

While rvalue reference returned from function is qualified as xvalue, which belongs to rvalue.

a function call or an overloaded operator expression, whose return type is rvalue reference to object, such as std::move(x);

Mairemaise answered 24/8, 2018 at 6:16 Comment(2)
Damn, this is so hard. I understand a little bit more now, thank you again, yuanyao.Hemidemisemiquaver
@Hemidemisemiquaver Try to always consider types and value categories separately, it'll become more clear.Mairemaise
M
11

The difference between the expressions q and std::forward<int>(q) is that the former is an lvalue, while the latter is an rvalue (of fundamental category xvalue).

I've addressed similar concerns in this answer: the point is that q as an expression is an lvalue, because it has a name. std::forward<int>(q) (or the equivalent std::move(q)) are expressions which don't have names, and since they return (unnamed) rvalue references, they are xvalues, which is a subcategory of rvalue and can thus bind to an rvalue reference.

Memoried answered 24/8, 2018 at 6:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.