Overloading for rvalue?

229 Views Asked by At

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.

2

There are 2 best solutions below

3
Lightness Races in Orbit On BEST ANSWER

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

0
bartop On

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
}