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?

gnat
  • 6,213
  • 108
  • 53
  • 73
Rick
  • 7,007
  • 2
  • 49
  • 79
  • Equivalent is not the same as identical. Hard coded values are not treated as regular variables – Eric Aug 24 '18 at 06:08
  • 1
    Don't think I could explain it better than Scott, so check out this: https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers – hassec Aug 24 '18 at 06:12
  • 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 void f(T&& x)` or `template void f(T&&... x)` – Daniel Schepler Aug 24 '18 at 17:22
  • Another possible surprise: `decltype(q)` is equivalent to `int&&` while `decltype((q))` is equivalent to `int&` – Daniel Schepler Aug 24 '18 at 17:23

2 Answers2

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);

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
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.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455