WEBVTT

00:00.090 --> 00:00.630
Hello again!

00:00.930 --> 00:06.570
In this video, we are going to look at move operators. Not to be confused with smooth operators.

00:07.020 --> 00:08.640
That is something quite different!

00:10.350 --> 00:17.040
So to recap, if we have a function which takes its argument by a const reference to lvalue, then

00:17.040 --> 00:20.640
we can pass either lvalues or rvalues, when we call this function.

00:21.600 --> 00:28.170
If we have an overload which takes the arguments by rvalue reference, and we pass a moveable rvalue,

00:28.650 --> 00:34.710
then the compiler will call this one instead. So we can get different behaviour, depending on whether the

00:34.710 --> 00:36.420
argument can be moved or not.

00:39.640 --> 00:43.120
One situation where this could come in useful, is the copy constructor.

00:43.780 --> 00:49.930
If you think back to our example, with a vector of 1 million strings, the copy constructor is going

00:49.930 --> 00:52.510
to call the copy constructor for every one of those strings.

00:52.960 --> 00:56.110
And that is going to create a lot of memory operations and data copying.

00:56.590 --> 01:02.290
But if we could just move the data, then we could save all that work and get much better performance.

01:06.120 --> 01:08.490
And in C++11, you can now do that.

01:08.760 --> 01:15.390
There are two new special member functions for that purpose: the move constructor and the move assignment

01:15.390 --> 01:15.910
operator.

01:16.500 --> 01:21.660
And these will be called automatically if the object which is passed is a moveable rvalue.

01:22.710 --> 01:28.860
These are overloads of the copy constructor for the cove constructor, and the assignment operator for

01:28.860 --> 01:30.300
the move assignment operator.

01:32.560 --> 01:40.360
These take their arguments by rvalue reference. And the copy assignment operator is now known as the

01:40.360 --> 01:42.220
assignment copy operator,

01:42.580 --> 01:47.680
to make clear we are talking about the version which takes an lvalue, and not the move assignment

01:47.680 --> 01:50.740
operator, which takes a movable rvalue.

01:54.430 --> 01:56.050
Let's look at the syntax for these.

01:56.440 --> 02:00.760
So the copy constructor takes its argument by const reference to lvalue.

02:01.540 --> 02:05.620
The Move constructor takes its argument by rvalue reference.

02:06.070 --> 02:12.320
And similarly for the assignment operators. You will notice that the rvalue reference is not const.

02:12.910 --> 02:14.890
That is because we are going to move from the argument.

02:15.190 --> 02:17.860
So we are going to modify it, so it cannot be const.

02:19.330 --> 02:22.540
The move operators are also declared as noexcept.

02:23.020 --> 02:25.510
And there is actually three reasons why you should do that.

02:27.780 --> 02:33.450
The first one is that generally move operators should just "move" data around.

02:33.480 --> 02:37.920
They should not allocate new resources or do other things, which could throw exceptions.

02:38.820 --> 02:44.010
The second one is, if you are halfway through a move operation, so you have some data in one object

02:44.010 --> 02:50.010
and the rest of the data in another object. If an exception is thrown, there is no easy way to recover

02:50.010 --> 02:50.490
from that.

02:51.120 --> 02:53.150
It is pretty hard to unscramble an egg.

02:54.730 --> 02:59.860
And the third reason is, if you want to put objects of your class inside one of the library containers,

03:00.370 --> 03:07.960
then the move operators must be noexcept. Otherwise, the code will not compile. And the return value

03:07.960 --> 03:09.760
from the assignment operators is the same.

03:10.000 --> 03:14.280
These both return the modified object by lvalue reference.

03:14.830 --> 03:16.840
And that will allow you to assign to it again.

03:21.380 --> 03:24.130
We're going to add some move operators to a class now.

03:24.930 --> 03:29.150
We are going to come back to our string-like class a bit later on in this section.

03:29.510 --> 03:31.610
But first, we will start with something a bit simpler.

03:32.330 --> 03:37.970
So we have a class which has a built in member, and a member which is a class object.

03:39.640 --> 03:42.460
So what does it mean, to move an object of this class?

03:42.970 --> 03:46.930
We need to move the int member, and the object member.

03:47.260 --> 03:51.710
So if we are calling the move constructor, we need to call the move constructor

03:51.730 --> 03:52.750
of these members.

03:57.530 --> 03:58.650
So let's see what we get.

03:59.300 --> 04:03.260
I have just put an empty shell for the class type, for the member.

04:03.590 --> 04:08.000
If you like, you could put some member functions in here, and get them to print out when they are called.

04:08.420 --> 04:09.590
So you can see what is happening.

04:11.150 --> 04:12.350
Here is the Test

04:12.350 --> 04:18.470
class, as we had it on the slide. It has a default constructor, so we can create objects of this class.

04:18.890 --> 04:21.380
And you might like to think about why I need to put that in there.

04:24.060 --> 04:24.750
The answer is, that

04:24.750 --> 04:30.450
I have defined a copy constructor, so the compiler will not synthesize the default constructor. For the

04:30.450 --> 04:31.260
copy constructor,

04:31.560 --> 04:34.530
it takes its argument by lvalue reference to const.

04:35.430 --> 04:42.180
Then it just calls the copy constructors for each member. With the move constructor,

04:42.540 --> 04:45.780
it takes its argument by rvalue reference, and it is

04:45.780 --> 04:55.590
noexcept. We can copy the built-in members. For the class object members, we need to call their move constructor.

04:56.100 --> 04:59.670
This means we need to provide an rvalue as argument.

05:00.720 --> 05:04.470
This member has a name, "m", and we can take its address.

05:04.830 --> 05:06.200
So this is actually an lvalue.

05:06.690 --> 05:08.880
So we need to convert this to an rvalue.

05:09.390 --> 05:12.360
So we need to put it inside a call to move.

05:12.990 --> 05:18.600
And then this will return the member as an rvalue. And that will call the move constructor for that

05:18.600 --> 05:19.050
object.

05:21.450 --> 05:24.810
For the assignment operator, now called the copy assignment operator.

05:25.290 --> 05:27.960
We take the arguments by const reference to lvalue.

05:28.650 --> 05:36.090
We check for self-assignment, and then we call the copy assignment operator for each of our members,

05:36.330 --> 05:41.610
with the corresponding member from the argument object as the argument to that call.

05:42.750 --> 05:46.290
And then we return the modified object, after it has been assigned to.

05:48.620 --> 05:50.330
For the move assignment operator,

05:50.870 --> 05:53.940
we take the argument by rvalue reference. It is declared

05:54.000 --> 06:00.890
noexcept. We have the self-assignment check, and then we call the move assignment operator, for each

06:00.890 --> 06:01.250
member.

06:02.360 --> 06:07.130
So we call that for the built-in type. And for the object,

06:07.130 --> 06:09.020
we need to provide an rvalue again.

06:09.440 --> 06:15.170
So we need to call move(), to get an rvalue, and that will call the move assignment operator for this

06:15.170 --> 06:15.530
class.

06:16.430 --> 06:18.530
And then we return the modified object again.

06:20.810 --> 06:23.390
In the main function, we create an object of this class.

06:24.230 --> 06:26.480
Then we use the copy constructor.

06:26.930 --> 06:28.400
This is going to be an lvalue.

06:28.970 --> 06:30.920
So this should call the copy constructor.

06:32.120 --> 06:35.390
Then we provide a temporary object, an rvalue.

06:35.390 --> 06:36.920
So that should call the move constructor.

06:38.360 --> 06:41.030
We have an lvalue which is cast to rvalue.

06:41.540 --> 06:43.760
So that should call the move constructor again.

06:45.140 --> 06:46.340
Then we do some assignments.

06:47.300 --> 06:49.970
We use an lvalue object for the assignment.

06:49.970 --> 06:52.400
So that should call the copy assignment operator.

06:52.970 --> 06:56.090
And then we use a temporary object, an rvalue.

06:56.750 --> 06:59.300
So that should call the move assignment operator.

07:00.260 --> 07:01.250
So let's see what we get.

07:05.940 --> 07:10.830
So this one with the lvalue calls the copy constructor.

07:11.910 --> 07:14.010
This one seems to have done nothing at all!

07:14.490 --> 07:18.600
I think the compiler has actually completely optimized out this statement. Which is a bit annoying,

07:18.600 --> 07:20.670
when you are trying to explain things and the compiler is

07:20.670 --> 07:21.210
too clever!

07:23.600 --> 07:24.230
This one,

07:24.260 --> 07:27.530
the one with the move(), that does call the move constructor.

07:28.130 --> 07:31.210
So the cast to rvalue. For the assignment,

07:31.220 --> 07:38.120
the lvalue calls the copy assignment operator, and the rvalue calls the move assignment operator.

07:44.520 --> 07:45.360
Just one more thing

07:45.360 --> 07:51.960
we need to know. When we have an inheritance hierarchy, and we are writing it move operators for a derived

07:51.960 --> 07:52.320
class,

07:52.740 --> 07:56.100
these need to call the corresponding versions in the base class.

07:56.760 --> 07:58.440
So we have done this with the copy constructor.

07:58.950 --> 08:04.920
We call the base class copy constructor with the same argument. For the move constructor,

08:04.950 --> 08:08.460
we need to make sure that the base class move constructor gets called.

08:09.030 --> 08:10.830
So we need to provide an rvalue.

08:11.760 --> 08:14.700
The argument to the move constructor is actually an lvalue.

08:15.030 --> 08:17.190
It has a name, and we could take its address.

08:17.850 --> 08:21.150
So we need to call move(), to cast it to an rvalue.

08:21.810 --> 08:24.870
And that will call the move constructor of the base class.

08:27.070 --> 08:31.630
And finally, just to confirm, this does work with classes which were written before C++11.

08:31.990 --> 08:36.430
A little anecdote, to finish off with. When Google was updating to C++11,

08:36.820 --> 08:41.680
the first thing they did was they took their old code base and ran it through a C++ 11 compiler, just

08:41.680 --> 08:42.350
to see what happened.

08:42.350 --> 08:43.630
So they did not actually change anything.

08:44.470 --> 08:51.280
And because the compiler was using move semantics with library objects, the performance was much faster.

08:52.270 --> 08:57.910
And that actually reduced their electricity bill by a six figure, or possibly seven figure number,

08:58.600 --> 08:59.410
in US dollars.

09:00.100 --> 09:03.010
So there you are. Move semantics will save the planet!

09:03.970 --> 09:05.470
Okay, so that is it for this video.

09:05.770 --> 09:06.580
I will see you next time.

09:06.580 --> 09:08.710
But until then, keep coding!
