Reputation: 763
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
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
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