You are on page 1of 18

05/05/2021 M.

2 — R-value references | Learn C++

M.2 — R-value references


BY ALEX ON FEBRUARY 20TH, 2017 | LAST MODIFIED BY ALEX ON DECEMBER 21ST, 2020

Way back in chapter 1, we mentioned l-values and r-values, and then told you not to worry that much about them.
That was fair advice prior to C++11. But understanding move semantics in C++11 requires a re-examination of the
topic. So let’s do that now.

L-values and r-values l-value r-value


Despite having the word “value” in their names, l-values and r-values are actually not properties of values, but rather,
properties of expressions.

Every expression in C++ has two properties: a type (which is used for type checking), and a value category (which is
used for certain kinds of syntax checking, such as whether the result of the expression can be assigned to). In C++03
and earlier, l-values and r-values were the only two value categories available.

The actual definition of which expressions are l-values and which are r-values is surprisingly complicated, so we’ll take
a simplified view of the subject that will largely suffice for our purposes.

It’s simplest to think of an l-value (also called a locator value) as a function or an object (or an expression that
evaluates to a function or object). All l-values have assigned memory addresses.

When l-values were originally defined, they were defined as “values that are suitable to be on the left-hand side of an
assignment expression”. However, later, the const keyword was added to the language, and l-values were split into
two sub-categories: modifiable l-values, which can be changed, and non-modifiable l-values, which are const.

It’s simplest to think of an r-value as “everything that is not an l-value”. This notably includes literals (e.g. 5),
temporary values (e.g. x+1), and anonymous objects (e.g. Fraction(5, 2)). r-values are typically evaluated for their
values, have expression scope (they die at the end of the expression they are in), and cannot be assigned to. This
non-assignment rule makes sense, because assigning a value applies a side-effect to the object. Since r-values have
expression scope, if we were to assign a value to an r-value, then the r-value would either go out of scope before we
had a chance to use the assigned value in the next expression (which makes the assignment useless) or we’d have to
use a variable with a side effect applied more than once in an expression (which by now you should know causes
undefined behavior!).

In order to support move semantics, C++11 introduces 3 new value categories: pr-values, x-values, and gl-values. We
will largely ignore these since understanding them isn’t necessary to learn about or use move semantics effectively. If
you’re interested, cppreference.com has an extensive list of expressions that qualify for each of the various value
categories, as well as more detail about them.

L-value references

Prior to C++11, only one type of reference existed in C++, and so it was just called a “reference”. However, in C++11,
it’s sometimes called an l-value reference. L-value references can only be initialized with modifiable l-values.

L-value reference Can be initialized with Can modify

Modifiable l-values Yes Yes

Non-modifiable l-values No No

R-values No No

L-value references to const objects can be initialized with l-values and r-values alike. However, those values can’t be
modified.

L-value reference to const Can be initialized with Can modify

Modifiable l-values Yes No

Non-modifiable l-values Yes No

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 1/18
05/05/2021 M.2 — R-value references | Learn C++
R-values Yes No

L-value references to const objects are particularly useful because they allow us to pass any type of argument (l-value
or r-value) into a function without making a copy of the argument.

R-value references

C++11 adds a new type of reference called an r-value reference. An r-value reference is a reference that is designed
to be initialized with an r-value (only). While an l-value reference is created using a single ampersand, an r-value
reference is created using a double ampersand:

1 int x{ 5 };
2 int &lref{ x }; // l-value reference initialized with l-value x
3 int &&rref{ 5 }; // r-value reference initialized with r-value 5

R-values references cannot be initialized with l-values.

R-value reference Can be initialized with Can modify

Modifiable l-values No No

Non-modifiable l-values No No

R-values Yes Yes

R-value reference to const Can be initialized with Can modify

Modifiable l-values No No

Non-modifiable l-values No No

R-values Yes No

R-value references have two properties that are useful. First, r-value references extend the lifespan of the object they
are initialized with to the lifespan of the r-value reference (l-value references to const objects can do this too). Second,
non-const r-value references allow you to modify the r-value!

Let’s take a look at some examples:

1 #include <iostream>
2
3 class Fraction
4 {
5 private:
6 int m_numerator;
7 int m_denominator;
8
9 public:
10 Fraction(int numerator = 0, int denominator = 1) :
11 m_numerator{ numerator }, m_denominator{ denominator }
12 {
13 }
14
15 friend std::ostream& operator<<(std::ostream& out, const Fraction &f1)
16 {
17 out << f1.m_numerator << '/' << f1.m_denominator;
18 return out;
19 }
20 };
21
22 int main()
23 {
24 auto &&rref{ Fraction{ 3, 5 } }; // r-value reference to temporary Fraction
25
26 // f1 of operator<< binds to the temporary, no copies are created.
27 std::cout << rref << '\n';
28
29 return 0;
30 } // rref (and the temporary Fraction) goes out of scope here

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 2/18
05/05/2021 M.2 — R-value references | Learn C++
This program prints:

3/5

As an anonymous object, Fraction(3, 5) would normally go out of scope at the end of the expression in which it is
defined. However, since we’re initializing an r-value reference with it, its duration is extended until the end of the block.
We can then use that r-value reference to print the Fraction’s value.

Now let’s take a look at a less intuitive example:

1 #include <iostream>
2
3 int main()
4 {
5 int &&rref{ 5 }; // because we're initializing an r-value reference with a literal, a
temporary with value 5 is created here
6 rref = 10;
7 std::cout << rref << '\n';
8
9 return 0;
10 }

This program prints:

10

While it may seem weird to initialize an r-value reference with a literal value and then be able to change that value,
when initializing an r-value with a literal, a temporary is constructed from the literal so that the reference is referencing
a temporary object, not a literal value.

R-value references are not very often used in either of the manners illustrated above.

R-value references as function parameters

R-value references are more often used as function parameters. This is most useful for function overloads when you
want to have different behavior for l-value and r-value arguments.

1 void fun(const int &lref) // l-value arguments will select this function
2 {
3 std::cout << "l-value reference to const\n";
4 }
5
6 void fun(int &&rref) // r-value arguments will select this function
7 {
8 std::cout << "r-value reference\n";
9 }
10
11 int main()
12 {
13 int x{ 5 };
14 fun(x); // l-value argument calls l-value version of function
15 fun(5); // r-value argument calls r-value version of function
16
17 return 0;
18 }

This prints:

l-value reference to const


r-value reference

As you can see, when passed an l-value, the overloaded function resolved to the version with the l-value reference.
When passed an r-value, the overloaded function resolved to the version with the r-value reference (this is considered
a better match than a l-value reference to const).

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 3/18
05/05/2021 M.2 — R-value references | Learn C++
Why would you ever want to do this? We’ll discuss this in more detail in the next lesson. Needless to say, it’s an
important part of move semantics.

One interesting note:

1 int &&ref{ 5 };
2 fun(ref);

actually calls the l-value version of the function! Although variable ref has type r-value reference to an integer, it is
actually an l-value itself (as are all named variables). The confusion stems from the use of the term r-value in two
different contexts. Think of it this way: Named-objects are l-values. Anonymous objects are r-values. The type of the
named object or anonymous object is independent from whether it’s an l-value or r-value. Or, put another way, if r-
value reference had been called anything else, this confusion wouldn’t exist.

Returning an r-value reference

You should almost never return an r-value reference, for the same reason you should almost never return an l-value
reference. In most cases, you’ll end up returning a hanging reference when the referenced object goes out of scope at
the end of the function.

Quiz time

1) State which of the following lettered statements will not compile:

1 int main()
2 {
3 int x{};
4
5 // l-value references
6 int &ref1{ x }; // A
7 int &ref2{ 5 }; // B
8
9 const int &ref3{ x }; // C
10 const int &ref4{ 5 }; // D
11
12 // r-value references
13 int &&ref5{ x }; // E
14 int &&ref6{ 5 }; // F
15
16 const int &&ref7{ x }; // G
17 const int &&ref8{ 5 }; // H
18
19 return 0;
20 }

Show Solution

M.3 -- Move constructors and move assignment

Index

M.1 -- Intro to smart pointers and move semantics

C++ TUTORIAL | PRINT THIS POST

59 comments to M.2 — R-value references


https://www.learncpp.com/cpp-tutorial/rvalue-references/ 4/18
05/05/2021 M.2 — R-value references | Learn C++

Quentin
February 27, 2021 at 4:48 am · Reply

Hi Alex,

" An r-value reference is a reference that is designed to be initialized with an r-value (only)."

But for code

1 int x{11};
2 auto &&rref{x};
3 std::cout << rref << '\n';

This works fine and rvalue reference binds to lvalue of x. I am confused by the result.

nascardriver
March 4, 2021 at 7:59 am · Reply

&&, when used with templates or `auto` can collapse. Your `auto` turns into `int&`, so you're
forming an r-value reference to an `int`-reference, which collapses to an `int`-reference.

1 #include <type_traits>
2
3 // @decltype() "returns" the type of @rref
4 // @std::is_same_v checks if 2 types are the same
5 std::cout << std::is_same_v<decltype(rref), int&> << '\n';

See https://en.cppreference.com/w/cpp/language/reference section "Reference collapsing"

yolo
March 23, 2021 at 12:18 pm · Reply

I don't really get why collapsing forms a problem. What kind of problems does it create?
The link doesn't say anything in particular.

nascardriver
March 24, 2021 at 8:06 am · Reply

Why do you think collapsing causes problems?

goiu
January 23, 2021 at 6:13 am · Reply

Hi there!
What is the difference between an anonymous object and a temporary value?

yolo
March 23, 2021 at 12:27 pm · Reply

Take it with a grain of salt. Anonymous objects can be temporary values but temporary values
cannot be anonymous objects. Anonymous objects can be

1 Class{5} //anonymous object. Gets destroyed immediately


2 Class class{5} // instead of this normal variable. Follows scope.

yeokaiwei
January 7, 2021 at 10:20 pm · Reply

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 5/18
05/05/2021 M.2 — R-value references | Learn C++
1. Feedback on "R-value references as function parameters" example
Based on the tutorial settings in Chapter 1, there will be warnings for the void fun() examples, while
using MSVS2019.

"C4100 'lref', 'rref' unreferenced formal parameters"

yeokaiwei
January 7, 2021 at 10:09 pm · Reply

1 #include <iostream>
2
3 int main()
4 {
5 int &&rref{ 5 }; // because we're initializing an r-value reference with a literal,
a temporary with value 5 is created here
6 rref = 10;
7 std::cout << rref << '\n';
8
9 return 0;
10 }

1 #include <iostream>
2
3 int main()
4 {
5 int rref{ 5 }; // I just removed the &&
6 rref = 10;
7 std::cout << rref << '\n';
8
9 return 0;
10 }

In the less intuitive example, what's the difference between the above code and the latter code?

Both print 10.

Hovsep_Papoyan
December 1, 2020 at 3:55 am · Reply

#include <iostream>

// example 1
/*
struct s {};

void fun(const s &lref) // l-value arguments will select this function


{
std::cout << "l-value reference to const\n";
}

void fun(s &&rref) // r-value arguments will select this function


{
std::cout << "r-value reference\n";
}

const s f()
{
return s{};
}

int main()
{
const s obj;
fun(std::move(obj)); // l-value reference

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 6/18
05/05/2021 M.2 — R-value references | Learn C++
fun(f()); // l-value reference
return 0;
}
*/

// example 2

void fun(const int &lref) // l-value arguments will select this function
{
std::cout << "l-value reference to const\n";
}

void fun(int &&rref) // r-value arguments will select this function


{
std::cout << "r-value reference\n";
}

const int f()


{
return 0;
}

int main()
{
const int obj{};
fun(std::move(obj)); // l-value reference to const
fun(f()); // r-value reference
return 0;
}

Pointless but nevertheless legal cases:


1) function that returns a const value, like f() above;
2) call std::move() on a const object, like above.

sv
August 1, 2020 at 1:00 am · Reply

i have a question
what would be the datatype of the anonymous object created for an expression?
for ex: x=y*10;
here the anonymous object(y*10)'s datatype? would it depend on the datatype of 'y' and '10' or 'x' ?

nascardriver
August 3, 2020 at 5:03 am · Reply

All of them.
`y * 10` can yield a different type than `y * 10.0`, so 10's type matters.
`y * 10` can yield a different type depending on what `y` is (eg. if `y` is a `double`, the result is a double. if
`y` is an `int`, the result is an `int`).
Then you need something that can be on the right-hand-side of an assignment to `x`. To call the assignment
operator or constructor of `x`'s type there might be another conversion.

sv
August 3, 2020 at 5:19 am · Reply

So if y=2*e32, where y is of type int, then x will be assigned garbage value as (y*10)
exceeds the limit of int... In that case one of the value should be casted to bigger
datatype...ok thanks

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 7/18
05/05/2021 M.2 — R-value references | Learn C++

Alven
July 24, 2020 at 7:53 pm · Reply

using Fun = std::function<void()>;


void fun(){}

int main()
{
int x{};
int &&ref1{ &x }; // A
Fun &&f = fun; // B
Fun ff = f; // C
Fun fff = std::move(f); //D
}
could this main function pass the compilation? &x is a lvalue or rvalue ? and what about fun?

nascardriver
July 26, 2020 at 1:47 am · Reply

`&x` is an rvalue. `fun` is an lvalue. There's a nice list on cppreference


https://en.cppreference.com/w/cpp/language/value_category

If you want to check if something compiles but you can't be bothered creating a file, you can use
godbolt.org

Alven
July 26, 2020 at 2:28 am · Reply

I also implemented a function to pass the fun function like that:


using Fun = std::function<void()>;
void fun(){}
void funPass(Fun && f)
{
Fun ff = f;
}

int main()
{
funPass(fun);
}

that works. if fun is a lvalue , why the funPass could accept it as its input parameter should be a
rValue? thanks.

nascardriver
July 26, 2020 at 2:31 am · Reply

You're not passing `fun` to `funPass` directly. You're constructing a temporary


`std::function` which is them passed to `funPass` by rvalue reference (That works,
because it's a temporary).

zara
May 29, 2020 at 9:14 am · Reply

Hi,
I was wondering in the following example, if 'x' is an l-value or r-value?

1 int x{10};

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 8/18
05/05/2021 M.2 — R-value references | Learn C++
2 int z {20};
3
4 int z=x;

How about the following? On the right-side of the 'greeting', are they r-value? how about the right-side of 'str', is it r-
value too?

1 std::string first_name {"hello"};


2 std::string last_name {"world"};
3
4 std::string greeting= first_name +last_name ;
5 std::string str= first_name ;

nascardriver
May 30, 2020 at 12:36 am · Reply

`x` and `first_name` are a lvalues. If you can modify something, it's a good sign that it's an
lvalue.
`first_name + last_name` is a prvalue, because `operator+(const std::string&, const std::string&)` returns by
value.

Michael
March 30, 2020 at 6:40 pm · Reply

Hi nascardriver,
Thanks for making this tutorial. It is very helpful.
Could you help me confirm that when function argument is a reference(regardless of either l-value or r-value ref)
the function overloading selection logic applied by C++ compiler is different from non-ref arguments?

However I am still very puzzled by the following example despite reading your explanation:

1 void fun(const int &lref) // l-value arguments will select this function // <-- label A
2 {
3 std::cout << "l-value reference to const\n";
4 }
5
6 void fun(int &&rref) // r-value arguments will select this function // <-- label B
7 {
8 std::cout << "r-value reference\n";
9 }
10
11 int main()
12 {
13 int &&ref{ 5 };
14 fun(ref); // selects the l-value ref version ?!
15 return 0;
16 }

The observation here is very difficult for me to grasp my head around.

For example, compiler should always pick the function with matching type signature, right?

1 void fun1(int y) {
2 std::cout << "y is of type int\n";
3 }
4
5 void fun1(double y) {
6 std::cout << "y is of type double\n";
7 }
8
9 int main() {
10 int y;
11 fun1(y); // selects the int version.
12 return 0;
13 }

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 9/18
05/05/2021 M.2 — R-value references | Learn C++
Here in my `fun1`, if the I cam calling with the symbol `y` of type double, it will always pick `void fun1(double x)`
because the type of argument x is double.

But in your example, the case is different despite that the symbole `x` has a matching type to function `label B`.

1 void fun2(const double& x, int y) {}


2 void fun2(double&& x, double y) {}
3
4 int main() {
5 int &&ref{5};
6 double y;
7 fun2(ref, y); // what version should this invoke?
8 }

nascardriver
March 31, 2020 at 5:35 am · Reply

There's nothing special about the overload selection. What you're observing is happening
because `ref` is not an r-value. It's a reference to an r-value.

1 #include <utility>
2
3 void fn(int&&){}
4
5 int main()
6 {
7 int&& i{ 5 };
8 fn(i); // Doesn't work.
9 fn(5); // ok
10 fn(std::move(i)); // ok
11 }

> what version should this invoke?


The second, because `ref` is an `int`. It has to be converted to a `double`. The conversion results in an r-
value. If `ref` was a `double`, the first overload would be used, because no conversion takes place and `ref`
is an l-value when you use it.

hellmet
November 30, 2019 at 6:06 am · Reply

I think the Fraction example (just after r-value) would be much more convincing to readers if the copy
constructor was explicitly deleted.

And for those looking for a reason,


B - r-values can be assigned only to 'const l-value references', not simple 'l-value references' (Lesson 6.11)
E/G - r-value references take only R-values (re-read this lesson!)

nascardriver
December 2, 2019 at 4:29 am · Reply

Adding unnecessary code while introducing something new can be confusing. I added a
comment instead, stating that no copies are made.

hellmet
December 2, 2019 at 4:56 am · Reply

You're right! Thank you!

Vasant
June 3, 2019 at 11:28 pm · Reply

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 10/18
05/05/2021 M.2 — R-value references | Learn C++
Hi Alex,
I removed the function which takes an lvalue reference and as expected, compiler reported an error
while invoking the function with an lvalue.

1 //void fun(const int &lref) // l-value arguments will select this function
2 //{
3 // std::cout << "l-value reference to const\n";
4 //}
5
6
7 void fun(int &&rref) // r-value arguments will select this function
8 {
9 std::cout << "r-value reference\n";
10 }
11
12 int main()
13 {
14 int x = 5;
15 fun(x); //DOESN'T COMPILE // l-value argument calls l-value version of function
16 fun(5); // r-value argument calls r-value version of function
17
18 return 0;
19 }
20
21 However, if i make the rvalue reference function a template function, invoking it with
an lvalue compiles. Can you please explain? Thanks.
22
23 template<typename T>
24 void fun(T &&rref) // r-value arguments will select this function
25 {
26 std::cout << rref;
27 std::cout << "r-value reference\n";
28 }
29
30 int main()
31 {
32 int x = 5;
33 fun(x); // l-value argument calls l-value version of function
34 fun(5); // r-value argument calls r-value version of function
35
36 return 0;
37 }

nascardriver
June 4, 2019 at 8:10 am · Reply

Hi!

Line 33 calls `fun<int&>(x)`. First a temporary reference to `x` is created, this reference can then be r-value-
referenced by @fun.
Since the non-template `fun` only accepts `int&&`, not `int&&&`, it won't compile. (You can't use `int&&&` as
a type manually (But you can if you use a type alias)).

Asgar
February 27, 2019 at 5:34 pm · Reply

Hi.

Is this an l-value?

1 new Fraction(3,5); // Does it not return an address?

Suppose there was a public member function named foo() in Fraction, could we then write a statement like below?

1 new Fraction(3,5)->foo(); // If we can do this, does it not indicate that new Fraction
(3,5) is an l-value?

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 11/18
05/05/2021 M.2 — R-value references | Learn C++

nascardriver
February 28, 2019 at 7:32 am · Reply

> Is this an l-value?


No, you cannot assign a value to it.

> could we then write a statement like below?


No. You're using @operator-> on an object of type @Fraction. @Fraction doesn't have an overloaded
@operator->. Even if it did, it couldn't return a type.
You can do

1 (new Fraction{ 3, 5 })->foo();

But this would cause a memory leak.

Asgar
February 28, 2019 at 9:36 am · Reply

Thanks, nascardriver. Yes, I meant to say

1 (new Fraction{ 3, 5 })->foo();

Oh, I see the memory leak now. But even then, is being able to be assigned with something a pre-
requisite for an l-value?

According to this page, all l-values have assigned memory addresses. So, isn't anything that evaluates
to a name or memory address an l-value?

nascardriver
March 2, 2019 at 3:57 am · Reply

> is being able to be assigned with something a pre-requisite for an l-value?


No. Without getting into the formal definition, an lvalue could be the left side of an
assignment, if there wasn't something preventing it (eg. const, deleted operator).
If assigning to an expression doesn't make any sense (eg. it's a literal or initialization), it isn't an
lvalue.

1 new Fraction{ 3, 5 } = 123;

is an initialization. Assigning to it doesn't make sense -> It's not an lvalue (It's a prvalue).

1 const int i{ 0 };
2 i = 3;

We cannot assign to @i, because it's const. But other than const, there's no logical reason not to
be able to assign to @i -> @i is an lvalue.

Sang Min Park


October 10, 2018 at 7:27 am · Reply

What do you mean when "However, since we’re initializing an r-value reference with it, its duration is
extended until the end of the block." (referring to this code -> Fraction &&rref = Fraction(3, 5);)
Even if you initialize Fraction rref = Fraction(3, 5), or Fraction& rref = Fraction(3, 5) doesn't it call the same
constructor and extends its lifetime as well?

Alex
October 11, 2018 at 12:25 pm · Reply

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 12/18
05/05/2021 M.2 — R-value references | Learn C++
1 Fraction rref = Fraction(3, 5);

Creates a temporary fraction and then uses the copy constructor to initialize rref. No extension necessary
since the temporary fraction can die after being copied.

1 Fraction& rref = Fraction(3, 5);

is illegal because you can't have a non-const l-value reference pointing to an r-value.

1 const Fraction& rref = Fraction(3, 5);


2 Fraction && rref = Fraction(3, 5);

Both of these extend the duration of the temporary Fraction.

TC
September 25, 2018 at 9:36 pm · Reply

Hi Alex,

As you can see, the r-value and the local class variable both produce the same outputs.

So what is the purpose of using r-value over the local class variable?

Thanks,

TC

1 int main()
2 {
3 Fraction &&rref = Fraction(3, 5); // r-value reference to temporary Fraction
4 Fraction ref(3, 5); // r-value reference to temporary Fraction
5 std::cout << rref << '\n';
6 std::cout << ref << '\n';
7
8 return 0;
9 } // rref (and the temporary Fraction) goes out of scope here

Alex
September 27, 2018 at 9:11 am · Reply

There is zero benefit to using references in this case. This is equally true of using a const l-
value reference in said case.

As the lesson notes, r-value references are more typically used as function parameters when you want to
differentiate function behaviors for l-value and r-value arguments.

TC
September 27, 2018 at 9:25 am · Reply

Thank you Alex

Topherno
May 16, 2018 at 12:20 pm · Reply

Hey Alex, quick question about these two sentences:

"First, r-value references extend the lifespan of the object they are initialized with to the lifespan of the r-value
reference. l-value references to const objects can do this too, but it’s far more useful for r-value references since r-
values have expression scope otherwise."

What exactly do you mean by "since r-values have expression scope otherwise"? If an object with expression
scope is initialized with a l-value ref to a const object, then its lifespan is also extended past expression scope, just
like with a r-value ref.

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 13/18
05/05/2021 M.2 — R-value references | Learn C++
Basically, what's confusing me is that with respect to extending lifespans, I don't see the advantage that r-value
refs have over l-value refs to const objects. On the other hand, the fact that r-value refs can modify their
corresponding r-values is a clear advantage. Thanks in advance :)

Alex
May 19, 2018 at 1:41 pm · Reply

By "expression scope", I mean that r-values are temporary values that typically only live until
the end of the expression they're in.

I take your second point -- there's no real advantage of r-values references over const l-value references in
terms of lifespan extension. I've removed the latter part of the original sentence accordingly.

Thanks for pointing that out!

Ryan
May 7, 2018 at 12:24 pm · Reply

"You should almost never return an r-value reference, for the same reason you should almost never
return an l-value reference. In most cases, you’ll end up returning a hanging reference when the
referenced object goes out of scope at the end of the function."

But I thought that "r-value references extend the lifespan of the object they are initialized with to the lifespan of the
r-value reference." So, shouldn't returning an r-value reference extend the lifespan of the r-value to the control of
the function caller code that is receiving the return value?

Alex
May 10, 2018 at 8:00 pm · Reply

Great question, and this is a confusing point.

Basically, if you do this:

1 Foo getFoo() // return by value


2 {
3 return Foo();
4 }
5
6 int main()
7 {
8 Foo &&ref = getFoo();
9 }

What happens is: inside getFoo() a temporary Foo is constructed. Since this function returns by value, a
copy of the Foo is returned, and the original Foo is destroyed. That returned Foo then has its lifetime
extended by being bound to the rvalue reference in main.

Now, look at this:

1 Foo&& getFoo() // changed to return r-value reference


2 {
3 return Foo();
4 }
5
6 int main()
7 {
8 Foo &&ref = getFoo();
9 }

In this example, inside getFoo() a temporary Foo is constructed. At the end of the function, this Foo gets
destroyed, and an r-value reference to the destroyed Foo is returned. Thus ref is left as a dangling pointer.

In the first case, the lifetime can be extended before the Foo is destroyed. In the second case, the Foo gets
destroyed before its lifetime can be extended.

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 14/18
05/05/2021 M.2 — R-value references | Learn C++

skv
February 24, 2018 at 12:08 pm · Reply

Good article!!!
Alex, could you plz explain why R-value references for 'user-defined data types' are modifiable
but not for 'built-in data types'?

nascardriver
February 25, 2018 at 4:54 am · Reply

Hi skv!

Mind giving an example? Alex showed that r-value references to built-in data types can be modified.

1 #include <iostream>
2
3 int main()
4 {
5 int &&rref = 5; // because we're initializing an r-value reference with a lit
eral, a temporary with value 5 is created here
6 rref = 10;
7 std::cout << rref;
8
9 return 0;
10 }

Prints 10

Isak
February 3, 2018 at 11:49 am · Reply

> should almost never return an l-value reference

shouldn't this be:

> should never return a local l-value refernece

Alex
February 5, 2018 at 10:03 am · Reply

Both are true. You should never return a reference to a local l-value, as it will go out of scope
and leave you with a hanging reference. You should almost never return an l-value reference
period, as there are only a few cases where it really makes sense to do so. The main time I see this occur is
when overloading operators, when we want to return either the implicit object (or std::ostream) by reference
so we can chain operators.

Isak
February 11, 2018 at 10:48 am · Reply

Thanks for explaining!

Summer
October 26, 2017 at 11:11 pm · Reply

Hey Alex, I have a question, why don't you mention the knowledge about 'universal reference' and
'perfect forwarding' in this chapter? I think this part is quite related. But I don't know if they are not
important at all.

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 15/18
05/05/2021 M.2 — R-value references | Learn C++
Thank you very much for the wonderful tutorial. Whenever I seek for something I come to your tutorial firstly to find
the answer, it is so clearly explained and solved me many problems. That's also the reason why I'm curious about
my question above.

Alex
October 30, 2017 at 9:35 am · Reply

I didn't include material on universal (forwarding) references or perfect forwarding because


these are slightly more advanced techniques and I simply haven't had time to write those
articles. I'd like to get to them some day.

Luhan
November 30, 2017 at 7:23 am · Reply

I've found a very good article about forward declaration Alex, maybe it can help you
someday to write something about it , and some other readers interested in it.

[link]http://thbecker.net/articles/rvalue_references/section_01.html.

Mohsen
August 15, 2017 at 9:07 am · Reply

Hi alex.thank you for this tutorial.


Can you introduce me a tutorial to learn Socket programming in c++?

Alex
August 16, 2017 at 10:32 am · Reply

I'm not aware of whether any good socket programming tutorials exist or not. Sorry!

Zoran
July 29, 2017 at 6:22 am · Reply

Hello, Alex,

I added a r-value reference y to your example and then called fun with it:

1 int main()
2 {
3 int x = 5;
4 fun(x); // l-value argument calls l-value version of function
5 fun(5); // r-value argument calls r-value version of function
6
7 int &&y = 5;
8 fun(y); // I expected this to call r-value version on fun.
9
10 return 0;
11 }

When I run the program, this is what I get:

l-value reference to const


r-value reference
l-value reference to const

I don't understand - why fun(y) calls l-value version?

Alex
July 29, 2017 at 10:31 pm · Reply

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 16/18
05/05/2021 M.2 — R-value references | Learn C++
Because a named r-value reference is actually an l-value. It has a name, and persists beyond
an expression.

Mike
January 17, 2019 at 3:07 am · Reply

^^This. If you haven't done so somewhere else (in which case, could you inform me
where), it might be worthwhile pointing this out either here, or on the next page). I was
scratching my head trying to figure out why some object I was initialising with an R-value reference
wasn't using the move constructor!

Alex
January 21, 2019 at 11:43 am · Reply

I added a note to this lesson. Thanks for the suggestion.

Martin
July 29, 2017 at 12:45 am · Reply

Instead of “everything this is not an l-value” you probably intended to write


“everything that is not an l-value”.

CA
July 13, 2017 at 7:23 am · Reply

What is the difference between const reference and lvalue reference to const objects? Can we use
the second one for the copy constructor? The reason that I am asking is that following code gives the
error "member function already defined"

1 class Foo {
2 public:
3 Foo() {
4 cout << "constructorn";
5 }
6
7 ~Foo() {
8 cout << "destructorn";
9 }
10
11 Foo(const Foo& f) {
12 cout << "copy constructorn";
13 }
14
15 Foo(Foo const& f) {
16 cout << "copy constructorn";
17 }
18 };
19
20
21 int main(int argc, char** argv) {
22 Foo f1;
23 return 0;
24 }

PS: Thanks Alex for this excellent tutorial. I almost done with all!!!

Alex
July 13, 2017 at 10:41 am · Reply

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 17/18
05/05/2021 M.2 — R-value references | Learn C++
A "const reference" is just a shorthand name for a "l-value reference to const object".

"const Foo&" and "Foo const&" are the same thing to the compiler.

Aakash
February 26, 2017 at 4:37 am · Reply

hello alex,
in the quiz, why the //D will compile

Alex
February 26, 2017 at 12:42 pm · Reply

Const l-value references can be initialized with either l-value or r-values.

aca4life
February 21, 2017 at 5:37 am · Reply

Hi Alex,

1) In the Fraction class: r-reference should have '&&' not '&'

2) Quiz: "State whether ... will compile or not!" or "State which .. won't compile!"

Alex
February 21, 2017 at 1:33 pm · Reply

Fixed and fixed! Thanks for the suggestions!

https://www.learncpp.com/cpp-tutorial/rvalue-references/ 18/18

You might also like