I think there's something I'm not quite understanding about rvalue references. Why does the following fail to compile (VS2012) with the error 'foo' : cannot convert parameter 1 from 'int' to 'int &&'
?
void foo(int &&) {} void bar(int &&x) { foo(x); };
I would have assumed that the type int &&
would be preserved when passed from bar into foo. Why does it get transformed into int
once inside the function body?
I know the answer is to use std::forward
:
void bar(int &&x) { foo(std::forward<int>(x)); }
so maybe I just don't have a clear grasp on why. (Also, why not std::move
?)
“l-value” refers to a memory location that identifies an object. “r-value” refers to the data value that is stored at some address in memory. References in C++ are nothing but the alternative to the already existing variable. They are declared using the '&' before the name of the variable.
An rvalue reference is a reference that will bind only to a temporary object. What do I mean? Prior to C++11, if you had a temporary object, you could use a "regular" or "lvalue reference" to bind it, but only if it was const: 1. 2.
An lvalue is an expression that yields an object reference, such as a variable name, an array subscript reference, a dereferenced pointer, or a function call that returns a reference. An lvalue always has a defined region of storage, so you can take its address. An rvalue is an expression that is not an lvalue.
By overloading a function to take a const lvalue reference or an rvalue reference, you can write code that distinguishes between non-modifiable objects (lvalues) and modifiable temporary values (rvalues). You can pass an object to a function that takes an rvalue reference unless the object is marked as const .
I always remember lvalue as a value that has a name or can be addressed. Since x has a name, it is passed as an lvalue. The purpose of reference to rvalue is to allow the function to completely clobber value in any way it sees fit. If we pass x by reference as in your example, then we have no way of knowing if is safe to do this:
void foo(int &&) {} void bar(int &&x) { foo(x); x.DoSomething(); // what could x be? };
Doing foo(std::move(x));
is explicitly telling the compiler that you are done with x and no longer need it. Without that move, bad things could happen to existing code. The std::move
is a safeguard.
std::forward
is used for perfect forwarding in templates.
Why does it get transformed into
int
once inside the function body?
It doesn't; it's still a reference to an rvalue.
When a name appears in an expression, it's an lvalue - even if it happens to be a reference to an rvalue. It can be converted into an rvalue if the expression requires that (i.e. if its value is needed); but it can't be bound to an rvalue reference.
So as you say, in order to bind it to another rvalue reference, you have to explicitly convert it to an unnamed rvalue. std::forward
and std::move
are convenient ways to do that.
Also, why not
std::move
?
Why not indeed? That would make more sense than std::forward
, which is intended for templates that don't know whether the argument is a reference.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With