-1

Below is a code demonstrating the question:

class X {};
X x;
X&& rvalue_ref = std::move(x);
static_assert(std::is_same<decltype(rvalue_ref), X&&>::value, "Different types"); //To be sure that type is X&&
void func(X&) {
    cout << "lvalue reference";
}

void func(X&&) {
    cout << "rvalue reference";
}
int main() {
    func(rvalue_ref);
}

The output:

lvalue reference

Could you please explain the reason for that? We have a variable of the type X&& and an overload for this type but this overload isn't called.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
beginpluses
  • 497
  • 2
  • 9

2 Answers2

2

References don't really work like that. Your argument rvalue_ref, despite its type, is an lvalue expression. You would have to do std::move again to make it an rvalue.

Types and value categories are two separate things, and it can be hard to remember that an expression naming an rvalue reference typed object is not automatically an rvalue expression.

Consider also:

void foo(T&& ref)
{
   bar(ref);             // lvalue arg
   bar(std::move(ref));  // rvalue arg
}

This is also mentioned on cppreference.com's "Value categories" article:

Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • But doesn't that break any rules for function overloads resolution? It looks weird that func(T) isn't called for a variable of the type T. – beginpluses May 24 '19 at 10:38
  • 2
    @beginpluses It is. Nothing's broken here, it's how C++ works. I concede that value categories can be confusing because they're "invisible". I'd rather like to see an IDE that can show you the value category of some expression on mouse-over, but I don't think any such thing exists yet. – Lightness Races in Orbit May 24 '19 at 10:52
  • _"for a variable of the type T"_ You should forget about variables and think about _expressions_. The expression you're passing to `ref` in my example is of type T, not of type T&&. That's just how it works. There's no problem with overloading, only with understanding expressions. – Lightness Races in Orbit May 24 '19 at 11:28
1

This is perfectly reasonable behaviour - rvalue reference is a lvalue in C++. Which means to get output

rvalue reference

you have to use std::move like that:

int main() {
    func(std::move(rvalue_ref));
}

This can be especially confusing when passing parameters to functions. For example, to pass argument as rvalue you have to use std::move:

void foo(bar &&baz) {
    function_taking_rvalue_reference(std::move(baz));// <--- this move is necessary
}
bartop
  • 9,971
  • 1
  • 23
  • 54