WEBVTT

00:00.120 --> 00:00.690
Hello again!

00:01.020 --> 00:06.690
In this video, we are going to look at lvalue and rvalue references. So we know about lvalues

00:06.690 --> 00:07.740
and rvalues.

00:08.370 --> 00:12.840
And now we are going to find out about lvalue references and rvalue references.

00:14.740 --> 00:17.890
Lvalue references are actually not very scary!

00:18.220 --> 00:23.110
They are just normal references. If you think about how these are implemented,

00:23.590 --> 00:24.790
they are done through pointers.

00:25.600 --> 00:32.110
If we have some reference to a variable, then that is actually implemented as a pointer to that variable.

00:32.770 --> 00:38.050
And when we go through the reference, we actually dereference the pointer. And the compiler will put

00:38.050 --> 00:40.000
in code to do all, this behind the scenes.

00:41.110 --> 00:45.220
Obviously, this requires that x has a name, and we can take its address.

00:45.790 --> 00:47.800
So that means that x must be an lvalue.

00:48.190 --> 00:50.590
So that is why these are called "lvalue references".

00:54.140 --> 01:00.920
As we saw in the last video, we cannot use an rvalue with these lvalue references, unless the reference

01:00.920 --> 01:05.540
is const. And again, the compiler will adds some extra code to make this work.

01:07.910 --> 01:14.840
In C++11, we now have the concept of a so-called "rvalue reference", which is not actually a reference

01:14.840 --> 01:15.200
at all.

01:15.470 --> 01:16.820
It is just a bit of syntax.

01:18.230 --> 01:21.560
The actual syntax we use, it looks a bit peculiar.

01:21.860 --> 01:26.180
We have these double ampersands. So this is not a reference to a reference.

01:26.840 --> 01:29.120
It is actually some special instruction to the compiler.

01:29.840 --> 01:31.880
So the compiler will check that

01:31.880 --> 01:34.700
the argument is a moveable value.

01:36.480 --> 01:43.890
For example, if we pass the value 2. Literals are rvalues, and built-in types are regarded as moveable,

01:44.460 --> 01:45.750
even though they actually get copied!

01:46.500 --> 01:47.940
So this will be accepted.

01:49.490 --> 01:54.740
On the other hand, if we have a variable y, this has a name, and we can take its address.

01:55.190 --> 01:59.300
So it is an lvalue, not an rvalue, and this will not compile.

02:00.380 --> 02:01.010
Let's check that.

02:02.240 --> 02:06.020
So here is a function which takes one of these "rvalue references".

02:07.130 --> 02:11.000
We can call it with 2 as argument, and that should work.

02:11.630 --> 02:11.930
Yep.

02:14.410 --> 02:20.470
If I pass an lvalue as argument, then the compiler does not like that.

02:20.800 --> 02:23.500
"You cannot bind an lvalue to an rvalue reference."

02:26.820 --> 02:29.310
So function arguments can be an rvalue reference.

02:30.500 --> 02:36.710
The object which is passed will be moved into the argument, if it is an rvalue and the type is moveable.

02:37.340 --> 02:43.340
Otherwise, the call does not compile. And this means we can actually overload on whether the

02:43.340 --> 02:48.920
argument is an rvalue reference or some type which takes an lvalue. So we can get to different behaviour

02:49.190 --> 02:52.220
depending on whether we pass an lvalue or an rvalue.

02:52.910 --> 02:56.750
And later on in this section, we will see that that can be useful sometimes.

03:00.750 --> 03:06.510
What happens if you have a function that takes an rvalue reference, and you want to pass an lvalue to

03:06.510 --> 03:06.690
it?

03:07.260 --> 03:12.660
Well, this must mean that you do not actually want to keep the data in the lvalue.

03:13.200 --> 03:18.810
If you remember the example we had, with the vector of 1 million strings, that vector was just created

03:18.810 --> 03:20.070
so it could be passed to the function.

03:20.490 --> 03:22.910
And after it was passed to the function, we we did not care.

03:22.920 --> 03:23.910
We do not want to know any more.

03:24.690 --> 03:30.360
So that would be a good candidate for moving from an lvalue into a function argument.

03:31.200 --> 03:32.250
So how can we do that?

03:34.160 --> 03:39.080
The C++ library provides a function for this, which is rather badly named.

03:39.080 --> 03:39.890
It is called move().

03:40.310 --> 03:42.050
This does not actually move anything.

03:42.410 --> 03:44.150
All it does is cast its argument.

03:44.990 --> 03:50.330
So if we go back to the function on the last slide, which takes an int as an rvalue reference.

03:50.930 --> 03:57.830
If we pass "y", and wrap it in a call to move(), then the return value from this will be "y" cast to an

03:57.830 --> 03:58.160
rvalue.

03:58.700 --> 04:01.480
And then we have a moveable rvalue. So the code

04:01.480 --> 04:02.600
would then compile.

04:05.130 --> 04:08.840
This is going to move the data from the argument "y" for into the function "x".

04:10.500 --> 04:16.140
So if we have our vector with 1 million strings, then doing something like this will move the strings

04:16.800 --> 04:20.640
from the local variable, into the function argument.

04:21.540 --> 04:22.880
So that means the original variable

04:22.890 --> 04:24.930
no longer has all those strings in it.

04:26.450 --> 04:29.420
Obviously, you should only do that if you do not care about that data.

04:30.200 --> 04:35.390
Once you call the function, then the data may be empty, or the vehicle may be unusable.

04:36.650 --> 04:40.670
If we want to use that variable again, it is not safe to do that, unless we reset it.

04:41.030 --> 04:43.460
So we need to reassign some data to it.

04:46.230 --> 04:46.520
Right.

04:46.640 --> 04:47.960
Let's look at some code again.

04:49.280 --> 04:56.930
Here is a function called test(), which takes a string by const reference to an lvalue. An lvalue reference

04:56.930 --> 04:57.410
to const.

04:58.250 --> 05:01.550
We have an overload, which takes one of these "rvalue references".

05:02.510 --> 05:06.530
These two functions are going to print out, so we know which one was called.

05:08.270 --> 05:10.430
And then we call them, with various arguments.

05:10.940 --> 05:17.900
So, with a temporary object; with a local variable; a reference to a local variable; and the result

05:17.990 --> 05:19.760
of calling move.

05:21.450 --> 05:21.930
So, what

05:21.930 --> 05:23.100
output do you expect to see?

05:28.020 --> 05:30.090
So the temporary object is an rvalue.

05:30.090 --> 05:32.220
So that calls the rvalue reference version.

05:32.610 --> 05:34.260
It is a moveable rvalue.

05:35.190 --> 05:39.810
The lvalue and the lvalue reference both call the lvalue reference version.

05:40.650 --> 05:46.020
And if we have the return value from move(), that calls the rvalue reference version as well.

05:47.010 --> 05:49.800
So that converts the lvalue into an rvalue.

05:51.520 --> 05:53.080
For the purposes of this function call.

05:57.170 --> 06:03.350
I am going to go on now, and declare this variable as an rvalue reference. And you are allowed to do that.

06:03.710 --> 06:05.180
So let's see what difference that makes.

06:06.080 --> 06:08.270
So we call the test function directly.

06:09.110 --> 06:12.050
And let's try calling it with the return value from move().

06:12.860 --> 06:15.170
So, any guesses what will happen here?

06:19.310 --> 06:22.250
Well, how many of you said that this is going to kill the lvalue version,

06:22.250 --> 06:22.850
I wonder.

06:24.150 --> 06:30.100
If you look at this, it is a variable which has a name, and it has an address.

06:30.120 --> 06:32.670
So we could do "&r", and that would be legal.

06:33.120 --> 06:38.370
So this is actually an lvalue, even though it is declared as an rvalue reference.

06:41.740 --> 06:43.330
So when we pass it to this function,

06:43.340 --> 06:45.220
it calls the version which takes an lvalue.

06:45.460 --> 06:49.210
If we want the rvalue version, then we have to cast it, by calling move().

06:53.380 --> 06:57.730
Well, there is quite a lot to think about here, so let's try and collect our thoughts a bit.

06:58.960 --> 07:01.990
If we have an argument type, which is just an lvalue.

07:02.050 --> 07:09.010
So there is no ampersand anywhere. We can pass an lvalue to this argument, and then the function will

07:09.010 --> 07:11.260
have a copy of the passed object's data.

07:11.800 --> 07:13.360
So that is just pass by value.

07:14.350 --> 07:21.340
If we pass a moveable rvalue, the object will be moved into the function, and the function now owns

07:21.340 --> 07:24.400
the data that used to belong to the object that was passed to it.

07:26.240 --> 07:30.140
If we have an an lvalue reference, we can pass an lvalue, but not an rvalue.

07:31.560 --> 07:37.440
The function has a reference to the passed object's data and it can use the reference to modify it.

07:40.140 --> 07:45.070
If we have a constable lvalue reference, we can pass either an lvalue or an rvalue.

07:45.750 --> 07:51.060
The function will have a reference to the original object's data, but it cannot modify it.

07:52.650 --> 07:58.590
And then finally, if the function argument is an rvalue reference, then we can pass a moveable

07:59.220 --> 08:00.690
value, but not an lvalue.

08:01.800 --> 08:07.380
The passed object will be moved into the function argument, and the function now owns the data that used

08:07.380 --> 08:08.670
to belong to that object.

08:09.960 --> 08:11.550
Okay, so that is it for this video.

08:11.970 --> 08:12.780
I will see you next time.

08:13.050 --> 08:15.090
Until then, keep coding!
