WEBVTT

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

00:00.960 --> 00:03.660
In this video, we are going to look at perfect forwarding.

00:04.560 --> 00:09.840
forwarding means that a function passes one or more of its arguments to another function.

00:10.800 --> 00:17.400
Here we have a function which takes an argument x, and it calls some function g, and it passes x to that

00:17.400 --> 00:17.910
function.

00:18.690 --> 00:22.830
So we say that f is forwarding the argument x to the function g.

00:25.340 --> 00:30.740
We can have so-called "perfect" forwarding, in which x has the same properties in g that it has in f.

00:31.640 --> 00:36.650
If x is modifiable in the body of f, then it can be modified by g.

00:37.430 --> 00:41.750
If x cannot be modified by f, then it cannot be modified by g.

00:42.440 --> 00:48.740
And if we moved an object into f's argument, then the same object gets moved into g's argument.

00:53.390 --> 00:57.050
Perfect forwarding is useful for writing functions which call constructors.

00:57.680 --> 01:00.830
An example of this is make_pair() from the standard library.

01:01.670 --> 01:09.080
A pair object has two members and the make_pair function will initialize those by perfectly forwarding

01:09.260 --> 01:09.980
the arguments

01:10.160 --> 01:11.450
that make_pair was given.

01:12.440 --> 01:18.080
If we pass an rvalue to make_pair, then the corresponding member will be initialized using the

01:18.080 --> 01:21.470
move constructor, and that is the most efficient way to do it.

01:24.820 --> 01:28.090
Another use is in templates with a variable number of arguments.

01:28.540 --> 01:33.100
We will look at those when we do compile-time programming, but usually you dispatch the arguments off to

01:33.100 --> 01:38.380
some other function. And, again, this will allow move operations, where they are available.

01:41.370 --> 01:45.780
So let's say we wants to implement this function f and function g.

01:46.170 --> 01:53.940
We have three functions g, which take the three possible alternatives: modifiable argument, unmodifiable

01:53.940 --> 02:00.100
argument and an argument which is moved into. And then we need some function f which can call these.

02:00.900 --> 02:06.240
The obvious way to do this is to write three overloads of f, with the same arguments and then call g.

02:06.900 --> 02:11.880
And obviously for the move version, we need to cast x to an rvalue.

02:13.290 --> 02:18.780
So we have three almost identical functions, so we could think about templates. And also, we know about

02:18.840 --> 02:22.560
forwarding references, so perhaps we can do something clever with those.

02:27.640 --> 02:29.210
So here is some code.

02:29.680 --> 02:33.100
We have a class, Test, and it does not really matter what it does.

02:33.730 --> 02:41.260
We have three overloaded functions g, which will take an object of this class by lvalue reference, const lvalue reference

02:41.260 --> 02:45.790
and rvalue reference, and it will tell us which version was called.

02:46.840 --> 02:53.560
Then we have our function f, which is a template function with a forwarding reference. And the compiler

02:53.560 --> 02:57.910
is going to instantiate different overloads of f(), depending on how we call it.

02:59.950 --> 03:06.430
If we pass an lvalue object, then T will be deduced as an lvalue reference to test.

03:07.060 --> 03:11.800
Then we have reference collapsing, and this will instantiate a function which takes lvalue reference

03:11.800 --> 03:13.160
to Test.

03:14.080 --> 03:19.570
So when we pass x to g(), that is going to call the version which takes lvalue reference to Test.

03:21.600 --> 03:28.650
If we pass a const lvalue to this function, T will be deduced as const lvalue reference.

03:29.220 --> 03:34.290
Then we have reference collapsing again, and x will be of type const lvalue reference.

03:34.950 --> 03:38.460
So when we call g, it calls the version which takes const lvalue reference.

03:40.290 --> 03:46.440
And if we pass an rvalue, then the compiler will instantiate this with an rvalue reference argument.

03:46.860 --> 03:48.900
So we should get the move version of g.

03:50.520 --> 03:51.600
So let's see what happens.

03:53.130 --> 03:56.070
So the lvalue argument gives us the modifiable version of g.

03:56.490 --> 04:02.760
The const lvalue argument gives us the immutable version of g, and the rvalue argument - ah, that calls

04:02.760 --> 04:04.140
the modifiable version of g.

04:04.530 --> 04:05.640
So that has not worked.

04:06.810 --> 04:07.800
So what has gone wrong?

04:08.370 --> 04:11.070
And if you have already worked it out, then give yourself a bonus point!

04:14.720 --> 04:20.000
When you are looking at template code and trying to make sense of it, it is often useful to try and

04:20.000 --> 04:22.100
imagine what the instantiated code looks like.

04:22.700 --> 04:25.610
So for the rvalue case, we would have something like this.

04:26.300 --> 04:29.390
The function will be instantiated with an rvalue reference.

04:29.900 --> 04:32.160
And then we call it by passing this argument.

04:33.260 --> 04:38.150
If we look at this argument x, it has a name. x, of course!

04:38.480 --> 04:40.820
And we could take its address if we wanted to.

04:41.150 --> 04:42.710
So this is actually an lvalue.

04:43.190 --> 04:45.590
So when we call g here, we are passing an lvalue.

04:45.920 --> 04:49.360
And that is why the compiler will put in a call to the lvalue version.

04:51.760 --> 04:55.660
So we really want to call the rvalue version of g, and we know how to do that.

04:55.660 --> 04:59.890
We can just cast the argument to an rvalue by calling move().

05:00.580 --> 05:01.390
So let's try that.

05:08.370 --> 05:09.930
So let's see if that works.

05:15.100 --> 05:18.910
And yes, the rvalue argument calls the move version of g.

05:19.480 --> 05:24.880
But if we look up here, we see that the lvalue argument calls the move version of g as well.

05:25.420 --> 05:26.320
And that is not good.

05:28.900 --> 05:33.250
When we call move here, we are saying, in effect, that we do not care what happens to x after

05:33.250 --> 05:34.570
this function returns.

05:35.710 --> 05:39.760
On the other hand, when we just do a normal function call, then we do care.

05:39.760 --> 05:40.720
Or we may care.

05:42.280 --> 05:47.800
We can reasonably assume the techs will still be in a usable state, when this function call returns.

05:49.480 --> 05:54.340
And if, in fact, the function call causes this variable thatto be moved into something else.

05:55.060 --> 06:00.010
So we no longer have access to the data, then that is rather undesirable.

06:01.540 --> 06:04.330
So this is what the code would look like for the lvalue version.

06:04.840 --> 06:11.500
We have an instantiation that takes the argument by lvalue reference and then, when we call g, we put

06:11.500 --> 06:13.150
the argument inside a call to move.

06:13.630 --> 06:15.760
So that is always going to call the rvalue version.

06:16.840 --> 06:18.010
We have got a bit of a problem here.

06:18.250 --> 06:25.870
We need to have something that will cast to an rvalue, but not if the argument is an lvalue reference.

06:27.650 --> 06:30.560
And in fact, the library provides something which does this.

06:30.860 --> 06:32.750
This is a function called forward().

06:33.740 --> 06:37.190
All it does is cast its argument to an an rvalue reference.

06:37.580 --> 06:39.800
And this all works because of reference collapsing.

06:40.460 --> 06:47.030
If the arguments is an lvalue reference, we have rvalue reference to lvalue reference, which collapses

06:47.030 --> 06:48.440
down to an lvalue reference.

06:48.980 --> 06:50.120
So that is unchanged.

06:51.320 --> 06:56.510
If we have an lvalue, then casting to rvalue reference gives rvalue reference.

06:57.320 --> 07:02.780
If we have an rvalue reference, then casting that gives rvalue reference to rvalue reference,

07:03.230 --> 07:06.290
which collapses down to an rvalue reference to the argument.

07:06.770 --> 07:11.270
So we always gets an rvalue reference, unless the argument is an lvalue reference.

07:13.070 --> 07:15.290
So forward does not cast it argument

07:15.530 --> 07:21.710
if the argument is an lvalue reference. And, by the way, the forward requires a template parameter.

07:22.280 --> 07:26.450
So we need to give the template type, which is not the case for move.

07:29.680 --> 07:30.970
So let's see if this helps.

07:34.360 --> 07:36.860
And I will try it without the template parameter.

07:39.160 --> 07:39.520
Okay.

07:39.520 --> 07:40.300
So that does not work.

07:44.530 --> 07:50.800
Now, what have we got? Lvalue argument calls modifiable version, const lvalue argument calls

07:50.800 --> 07:57.660
immutable version, and rvalue argument calls the move version. (Ah, at last!)

07:59.320 --> 08:04.840
Okay, so this may all seem a bit elaborate. And if you only right applications, then you may never

08:04.840 --> 08:06.070
actually need to use this.

08:07.120 --> 08:11.230
But it is a useful technique if you are writing library code, because it is a template.

08:11.230 --> 08:16.840
So you do not need to actually write very much code, and it will automatically perform move operations

08:16.840 --> 08:17.740
if they are available.

08:18.190 --> 08:19.780
So that is always the most efficient form.

08:20.110 --> 08:22.660
So it is efficient, both in development time and run time.

08:23.620 --> 08:25.030
Okay, so that is it for this video.

08:25.480 --> 08:26.290
I will see you next time.

08:26.290 --> 08:28.300
But until then, keep coding!
