WEBVTT

00:00.210 --> 00:00.810
Hello again!

00:01.080 --> 00:05.010
In this video, we are going to look at function arguments and move semantics.

00:05.460 --> 00:11.460
We are going to try and find the fastest way of passing an object to a function, in C++ 11.

00:13.770 --> 00:19.110
Before we do that, there are quite a few ways of passing objects, so let's make sure we fully understand

00:19.110 --> 00:22.440
how they all work. When we have pass by value,

00:22.440 --> 00:27.510
the copy constructor is called. The function has a variable which is a copy of the caller's

00:27.510 --> 00:34.320
object. And the object which is passed is not modified, because it is a different object.

00:34.920 --> 00:41.400
When we pass by const reference, the function has access to the caller's object, but it can only read

00:41.400 --> 00:42.900
it, and it cannot modify it.

00:44.290 --> 00:49.810
If we pass by non-const reference or by address, then the function has full access to the caller's

00:49.810 --> 00:51.850
object, and it can modify it.

00:52.630 --> 00:56.440
And finally, with pass by move, the move constructor is called.

00:56.890 --> 01:01.330
This will move the data, from the passed object, into the function's object.

01:02.020 --> 01:03.640
The function will now own that data.

01:04.120 --> 01:07.420
And the caller's object is in a state where it cannot be used.

01:08.200 --> 01:11.200
It can be destroyed. If you want to use it again,

01:11.260 --> 01:13.570
you have to assign to it, or re-initialize it.

01:14.230 --> 01:20.230
If we are passing an object to a function, before C++11, and the function is not going to modify it,

01:20.920 --> 01:23.920
the usual advice is that you should pass by value.

01:23.920 --> 01:30.640
If it's a built in type or a small object, maybe two or three members which are built-in types. And

01:30.640 --> 01:32.950
anything larger should be passed by const reference.

01:33.400 --> 01:37.450
And that was the most efficient way to pass objects, before C++11.

01:38.170 --> 01:39.820
So does that still apply?

01:40.240 --> 01:43.030
Is this still good advice in C++11?

01:44.140 --> 01:44.830
Let's find out.

01:49.070 --> 01:52.160
I think the best way to approach this is by looking at constructors.

01:52.820 --> 01:57.950
Let's imagine that we have a class which has a string member, which is a moveable object.

01:58.550 --> 02:04.700
So we could initialize this, using lvalues or rvalues, and we want to do this in the most efficient

02:04.760 --> 02:05.540
way possible.

02:06.380 --> 02:07.370
So how do we do that?

02:08.720 --> 02:10.730
So we need to find out what goes in here.

02:10.760 --> 02:13.470
Is it going to be by value? Or const reference?

02:13.490 --> 02:14.660
Or maybe something else?

02:16.130 --> 02:16.840
Let's find out.

02:19.630 --> 02:21.050
Let's go through the possibilities.

02:21.110 --> 02:23.590
So the first one is passing by const reference.

02:25.840 --> 02:28.120
In both cases, lvalue or rvalue,

02:28.480 --> 02:33.490
the passed object will be bound to this reference, or rather, the reference is bound to the object.

02:34.750 --> 02:38.170
So this argument is going to be a reference to the caller's object.

02:40.000 --> 02:43.840
Then we use that to initialize the data member of the class.

02:44.560 --> 02:48.250
And that's going to be by copying because it's a value member.

02:49.990 --> 02:54.330
So we have just one copy operation, where the argument is copied into the data member.

02:54.970 --> 03:01.360
There is no copy for the actual argument, because these are bound by reference to the passed object.

03:03.560 --> 03:06.320
The next one is pass by value, which can also be pass by move.

03:07.340 --> 03:10.850
If we have an lvalue argument, then this is going to be passed by value.

03:11.210 --> 03:15.860
So the object is going to be copied into this argument, and then the object will be copied into the

03:15.860 --> 03:16.220
member.

03:16.610 --> 03:18.620
So that is two copy operations,

03:19.010 --> 03:20.480
if we have an lvalue argument.

03:21.690 --> 03:27.960
For an rvalue, that will be moved into the argument, and then the argument will be copied into the

03:27.960 --> 03:28.320
member.

03:28.800 --> 03:32.100
So that is one move operation, and one copy operation.

03:34.380 --> 03:39.690
If we are very cunning, we could look at this string argument. Aand we could say, this is actually a

03:39.690 --> 03:42.450
copy of the caller's object, so we can do what we like with this.

03:42.990 --> 03:46.500
So if we move from it, it is not going to cause the caller any problems.

03:46.920 --> 03:50.280
So we could actually move into the data member of the class.

03:51.930 --> 03:58.620
So if we have an lvalue, this will be copied into the argument, and then this copy can be moved into

03:58.620 --> 03:59.580
the data member.

04:01.710 --> 04:08.160
If we have an rvalue object passed, that can be moved into the argument, and then the argument can

04:08.160 --> 04:09.180
be moved into the member.

04:09.600 --> 04:14.130
So that is two move operations. And just to demonstrate you can actually do this...

04:15.570 --> 04:15.740
Yep.

04:15.850 --> 04:16.530
That compiles.

04:18.260 --> 04:20.470
And then, finally, pass by rvalue reference.

04:20.480 --> 04:22.850
And I hope that some of you thought about this.

04:25.650 --> 04:30.960
If the passed object is an rvalue, then it can be moved into this argument, and then the argument

04:30.960 --> 04:32.460
can be moved into the member.

04:33.330 --> 04:37.860
Unfortunately, if the past object is an lvalue, then you are not allowed to do that.

04:38.340 --> 04:39.510
You will get a compiler error.

04:42.030 --> 04:44.940
So let's go through and add up all the work that's involved.

04:45.480 --> 04:50.400
If we pass by const reference, there is one copy, for lvalue or rvalue objects.

04:51.240 --> 04:57.540
If we pass by value/by move, there is two copies for an lvalue object and one move and one copy for

04:57.540 --> 04:58.560
an rvalue object.

04:59.640 --> 05:05.250
If we pass by value, and then move the argument into the data member, then there is one copy and one

05:05.250 --> 05:12.420
move for an lvalue, and that is two moves for an rvalue object. And then, passing by rvalue reference.

05:12.840 --> 05:14.610
There is one move with an rvalue.

05:14.610 --> 05:15.720
So that is the winner

05:16.170 --> 05:21.510
for an rvalue. Usually, moves are much quicker than copies. But it is not allowed at all for

05:21.510 --> 05:21.930
lvalues.

05:22.500 --> 05:28.290
So that is not the answer, or at least not the complete answer. For an lvalue,

05:28.680 --> 05:30.690
the pass by const reference is the winner.

05:31.980 --> 05:33.510
So which one do we choose? Or -

05:33.750 --> 05:35.730
Well, who says we just have to choose one?

05:35.970 --> 05:36.990
Why can we not have two?

05:37.410 --> 05:40.020
So we could have one for lvalues and one for rvalues.

05:40.680 --> 05:41.370
Does that work?

05:44.830 --> 05:46.350
And yes, in fact, it does work.

05:46.360 --> 05:52.780
We can have overloads, one which takes a const reference, and one which takes an rvalue reference.

05:53.650 --> 05:59.170
If we pass an lvalue, then only this version can be called, because lvalues cannot bind to an

05:59.170 --> 06:00.190
rvalue reference.

06:00.760 --> 06:05.920
If we have an rvalue object passed, then either one of these could be called, because you can bind

06:06.550 --> 06:09.520
an rvalue to a const lvalue reference.

06:10.000 --> 06:14.710
However, the compiler will choose the best match, and the compiler will decide that

06:14.710 --> 06:17.650
an rvalue reference is a better match than a const

06:17.650 --> 06:18.730
reference to an lvalue.

06:19.360 --> 06:21.550
So this will be called whenever an rvalue is passed.

06:22.240 --> 06:24.790
So for lvalues, this requires one copy operation.

06:25.060 --> 06:27.940
And for rvalues, we just need one move operation.

06:28.750 --> 06:29.110
So.

06:30.110 --> 06:30.890
That is the solution.

06:31.730 --> 06:33.560
Well, there is one slight drawback.

06:34.370 --> 06:38.090
If we have a construction with an empty body, it does not really matter that we have two functions.

06:38.990 --> 06:43.190
If we have something which is not trivial, then we have to write the same code twice.

06:43.550 --> 06:47.690
The function bodies will be exactly the same, but the function arguments will be different.

06:48.410 --> 06:49.820
So that is a bit unsatisfactory.

06:50.330 --> 06:55.100
But C++11 does actually provide a way around that, and we will look at that next time.

06:55.730 --> 06:56.990
But for now, that is all.

06:57.560 --> 06:58.220
I will see you later.

06:58.220 --> 07:00.410
But until then, keep coding!
