CodingFanSteve
CodingFanSteve

Reputation: 763

C++11: Why rvalue reference parameter implicitly converted to lvalue

Following is the simplistic code of my problem,

void overloaded (int&& x) {
  cout << "[rvalue]";
}

template <class T>
void fn (T&& x) {
  overloaded(x);
}

int main() {
    fn(0);
    return 0;
}

I got a compile error

cannot bind ‘int’ lvalue to ‘int&&

  overloaded(x);

I am confused here, x is passed as a rvalue reference into fn(). But why the overload() call in fn() complains x is a lvalue?

Upvotes: 4

Views: 3749

Answers (2)

Kerrek SB
Kerrek SB

Reputation: 476980

You're not "passing x as an rvalue reference", since that statement doesn't even really make sense.

Here are a selection of facts about the taxonomy of types and expressions in C++:

  • There are object types and there are reference types. (There are also a few other kinds of types.)

  • Variables can be either objects or references.

  • Expressions have types, those types are (almost) always object types, never references. (That's why your statement of "passing something as a reference" doesn't make sense; you pass arguments and arguments are always expressions.) Expressions can be lvalues, xvalues or prvalues.

  • Lvalue references bind to lvalues. Rvalue references bind to rvalues. (An rvalue is either a prvalue or an xvalue.)

  • An id-expression naming a variable or parameter is an lvalue. (It's a "thing with a name", or "a location", if you will.)

Hence in the expression overloaded(x), the subexpression x is an lvalue, and it does not bind to the function that expects an rvalue (on account of its rvalue reference parameter).

The "l" and "r" in "lvalue reference" and "rvalue reference" refers to the categories of values to which the reference can bind, not to the category of the id-expression naming a variable of this reference type.

You can convert an lvalue to an rvalue by casting it to an xvalue; this is conveniently encapsulated into the type-deducing cast helper std::move.

Upvotes: 8

ShadowRanger
ShadowRanger

Reputation: 155363

One, the x argument to fn isn't an r-value reference, it's a "universal reference" (yes, this is rather confusing).

Two, the moment you give an object a name, that name is not an r-value unless explicitly "fixed", either with std::move (to make it an r-value reference, always), or with std::forward (to convert it back to its original type in the case of universal references). If you want to avoid the complaint, use std::forward to forward as the original type:

template <class T>
void fn (T&& x) {
  overloaded(std::forward<T>(x));
}

Upvotes: 4

Related Questions