Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding rvalue references

Tags:

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?)

like image 864
moswald Avatar asked Sep 26 '12 16:09

moswald


People also ask

Is a reference an R value?

“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.

What is R value reference in C++11?

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.

Is an rvalue reference an lvalue?

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.

Can you pass an lvalue to an rvalue reference?

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 .


2 Answers

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.

like image 196
SaulBack Avatar answered Nov 03 '22 18:11

SaulBack


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.

like image 23
Mike Seymour Avatar answered Nov 03 '22 19:11

Mike Seymour