WEBVTT

00:00.180 --> 00:00.780
Hello again!

00:01.080 --> 00:03.930
In this video, we are going to look at forwarding references.

00:05.370 --> 00:08.730
First, we are going to have a slight detour and talk about nested references,

00:08.730 --> 00:09.690
what happens with those.

00:10.350 --> 00:15.450
We cannot actually declare something as a nested reference, because the syntax does not exist.

00:15.810 --> 00:17.010
The compiler will not let us.

00:17.970 --> 00:23.160
However, it is possible for these to arise internally in the compiler, and this can happen with a type

00:23.160 --> 00:25.290
alias, or a template parameter.

00:29.910 --> 00:33.570
Let's look at the type alias first, because that is a bit easier to understand.

00:34.800 --> 00:38.100
We have a type alias here, for a reference to int.

00:38.700 --> 00:43.860
So whenever we put "int_ref", the compiler will replace this by reference to int.

00:45.330 --> 00:50.850
So we can create a variable which is of type "int_ref" and bind it to an inch, and that will just be

00:50.850 --> 00:51.480
a reference to int.

00:52.470 --> 00:53.640
So that is the type of "j".

00:54.330 --> 00:57.500
We can also declare something as a reference to an "int_ref".

00:58.230 --> 01:03.240
So in this case, "rj" is a reference to something which is a reference to int.

01:03.960 --> 01:05.130
So what happens here?

01:06.630 --> 01:09.690
The compile will perform something called "reference collapsing".

01:10.080 --> 01:14.100
So these multiple references will be collapsed into a single reference.

01:14.520 --> 01:17.580
So "rj" is actually just a reference to int.

01:21.500 --> 01:22.850
So let's try this out.

01:23.870 --> 01:27.170
So here are the various variables that we created on the slide.

01:27.530 --> 01:30.710
Actually, let's first of all check that we cannot create a reference to a reference.

01:33.870 --> 01:37.040
So there we are. "Reference to reference is illegal".

01:44.000 --> 01:47.040
So we have this "rj", which is a reference to an int_ref.

01:47.060 --> 01:48.920
So it is a reference to a reference to an int.

01:49.670 --> 01:52.910
The compiler will collapse this down, to a single reference to an inch.

01:53.420 --> 01:57.530
And to check this, we have a function which takes a reference to int as argument.

01:58.490 --> 02:01.760
So if this has been collapsed, then we should be able to call this function.

02:02.240 --> 02:06.740
On the other hand, if "rj" is not a reference to int, then it will not compile.

02:10.000 --> 02:11.200
And there we are, it does work.

02:11.500 --> 02:13.480
So the compiler has collapsed the reference.

02:19.470 --> 02:20.760
That was all C++98,

02:20.760 --> 02:23.610
so we just had to worry about lvalue references in those days.

02:24.690 --> 02:27.720
With C++11, we also have rvalue references.

02:28.440 --> 02:31.530
The rules for reference collapsing are a bit more complex.

02:31.980 --> 02:33.630
They are basically an "and" operation.

02:34.260 --> 02:39.060
If we have an rvalue reference to something which is an rvalue reference, then that collapses

02:39.060 --> 02:40.590
down into an rvalue reference.

02:41.640 --> 02:45.840
If we have an lvalue reference anywhere, then that collapses down to an lvalue reference.

02:46.500 --> 02:52.410
So, lvalue reference to lvalue reference, rvalue reference to lvalue reference, rvalue reference to

02:52.420 --> 02:55.860
lvalue reference, all collapse down to lvalue reference to int.

02:56.670 --> 03:01.530
And if we have an rvalue reference to rvalue reference, only then does it collapse down to rvalue

03:01.530 --> 03:02.010
reference.

03:05.290 --> 03:11.980
Well, now we can finish digressing and we can get back on to looking at forwarding references. When

03:11.980 --> 03:14.590
we have had an argument followed by a double ampersand,

03:14.890 --> 03:17.260
so far it has always been a specific type.

03:17.770 --> 03:24.520
So if we have something like this, x is a rvalue reference to an object of type Test. Some class.

03:26.090 --> 03:27.830
It can only be bound to an on value.

03:27.860 --> 03:31.730
So we must either pass an xvalue or a prvalue when we call this function.

03:32.150 --> 03:34.910
If we are using the C++17 terminology.

03:37.430 --> 03:42.710
If we have a template parameter followed by a double ampersand, then that is completely different.

03:43.640 --> 03:51.860
This is known as a forwarding reference or sometimes a universal reference, and this parameter x can be bound

03:51.860 --> 03:53.900
to either an rvalue or an lvalue.

03:54.320 --> 03:55.670
So that's why it is called universal.

03:55.700 --> 03:56.690
It can bind to anything.

03:57.680 --> 04:00.770
So if we call this function, the compiler has to instantiate it.

04:01.190 --> 04:07.250
The compiler has to decide what to type to use for the template parameter T, and also the type of the

04:07.250 --> 04:08.450
argument parameter x.

04:09.380 --> 04:10.430
So how does that work?

04:13.730 --> 04:16.250
The compiler will look at the object that is being passed.

04:16.580 --> 04:18.470
Is this an lvalue or an rvalue?

04:18.740 --> 04:20.480
And then it will make the decision.

04:22.160 --> 04:29.540
If the passed object is an lvalue, then the template parameter "T" will be deduced as an lvalue reference

04:29.600 --> 04:30.500
to that type.

04:31.250 --> 04:33.920
So T will be lvalue reference to Test.

04:34.850 --> 04:37.340
And then x is now an rvalue reference to T.

04:37.820 --> 04:41.030
So, that is an rvalue reference to an lvalue reference to test.

04:41.990 --> 04:47.000
Then reference collapsing will occur, and that will collapse down to an lvalue reference to Test.

04:47.750 --> 04:53.200
So X will be an lvalue reference, even if we just passed an lvalue and not a reference in the actual object

04:53.210 --> 04:53.780
that we passed.

04:55.210 --> 05:00.250
If we pass an rvalue object, then the template parameter will be the actual type of the object,

05:00.610 --> 05:01.360
not a reference.

05:02.650 --> 05:06.280
X will be an rvalue reference to that, which is just a normal object.

05:06.370 --> 05:08.710
No reference, so there is new reference collapsing.

05:09.580 --> 05:12.850
And then the result  will be an rvalue reference to Test.

05:15.400 --> 05:16.960
So let's go through this in the compiler.

05:17.290 --> 05:20.650
We have our class "Test", which does not need to do anything for this example.

05:21.310 --> 05:27.250
We have our function which takes a forwarding reference as argument, and then we have a main() function

05:27.250 --> 05:30.010
where we call this function with various objects.

05:31.690 --> 05:34.810
First of all, we call this with a normal variable, an lvalue.

05:35.860 --> 05:43.000
The template parameter type will be lvalue reference to Test, and then x will be an rvalue reference

05:43.000 --> 05:49.190
to this. The rvalue reference to lvalue reference will collapse down, to lvalue reference to Test.

05:49.780 --> 05:53.860
So the compiler will instantiate a function which takes an lvalue reference as argument.

05:55.360 --> 05:59.290
If we pass an lvalue object, then it works exactly the same.

05:59.830 --> 06:07.240
We have template parameter type is lvalue reference to Test, x is in rvalue reference to that, and the result

06:07.720 --> 06:09.100
is lvalue to test.

06:09.790 --> 06:11.710
So it will instantiate the same function again.

06:13.690 --> 06:20.680
If we pass an rvalue object, then the template parameter is a Test, not a reference, just a Test.

06:21.250 --> 06:23.560
And x will be an rvalue reference to that.

06:24.100 --> 06:26.140
So that is rvalue reference to Test.

06:26.770 --> 06:31.900
So if we pass in rvalue object, then there is no reference collapsing. The compiler

06:31.900 --> 06:36.310
will instantiate a function which takes an rvalue reference to Test as its argument.

06:39.700 --> 06:46.270
So this is all a bit technical. Is it actually useful? If you remember the last video, we were looking

06:46.270 --> 06:51.760
at the most efficient way to pass an object without modifying it, both by lvalue and by rvalue.

06:52.450 --> 06:58.420
And we decided that the best way is to have two overloads: one which takes an lvalue reference,

06:58.780 --> 07:00.460
and one which takes an rvalue reference.

07:00.880 --> 07:03.010
But the actual function bodies are exactly the same.

07:03.610 --> 07:07.060
So apart from the argument type, these functions are identical.

07:07.930 --> 07:11.770
So, two functions is a bit of a nuisance, but maybe you can live without that.

07:12.160 --> 07:16.810
Say, for example, you have four arguments, and everyone of them can be either an lvalue or an rvalue.

07:17.410 --> 07:21.310
So I think that's 20 different overloads that you'd have to write, with

07:21.670 --> 07:23.980
all exactly the same function bodies, but different arguments.

07:24.580 --> 07:28.660
And then you get things like default arguments or functions with variable numbers of arguments.

07:29.230 --> 07:31.240
And you can see this could easily turn into a nightmare.

07:32.940 --> 07:38.850
If instead you write a function which takes a forwarding reference, then the compiler will instantiate

07:39.270 --> 07:42.210
the correct argument types, depending on what objects we pass.

07:42.960 --> 07:48.720
And all the hard work is done for us, which is always nice. If you can get the compiler to do the work

07:48.720 --> 07:50.550
for you, instead of having to write the code yourself,

07:50.550 --> 07:54.690
that is always a good thing. And that is actually a direction that C++ is going in.

07:55.860 --> 07:57.330
Okay, so that is it for this feature.

07:57.750 --> 07:58.620
I will see you next time.

07:58.620 --> 08:00.690
But until then, keep coding!
