WEBVTT

00:00.090 --> 00:09.060
Hello again! In this video, we are going to look at move-only types and RAII. In C++, you can create a move-

00:09.060 --> 00:16.860
only type. So this is something which can be moved but not copied. And to do that, you declare the copy

00:16.860 --> 00:24.990
constructor and the copy assignment operator as deleted, and then you implement the move operators and

00:24.990 --> 00:27.810
the other member functions in the usual way.

00:32.410 --> 00:36.440
Let's try this out with the Test class that we were using in an earlier video.

00:37.180 --> 00:42.640
So we have the copy constructor and the copy assignment operator declared as "=delete".

00:43.480 --> 00:46.000
This does not mean that the functions are non-existent!

00:46.600 --> 00:50.410
It means that the compiler can decide that these are the best match.

00:50.860 --> 00:53.470
But when they actually get called, there is a compiler error.

00:54.010 --> 00:58.030
So these are functions which exist and do not exist at the same time.

00:58.540 --> 01:00.700
They are "Schroedinger's cat" functions, if you like.

01:01.810 --> 01:05.860
Then we have the same move constructor and move assignment operator as we had before.

01:06.700 --> 01:12.030
We also have the same main() function, but with the copy constructor and the copy assignment operator

01:12.030 --> 01:13.780
calls commented out.

01:15.400 --> 01:21.400
So if we run this, we get the same results for the move operators as we had before.

01:21.940 --> 01:25.960
We still get this statement being optimized out, which is still annoying me!

01:28.530 --> 01:34.200
If I uncomment the copy constructor call, what will happen?

01:36.840 --> 01:41.430
We get a compiler error. "Attempting to reference a deleted function".

01:44.700 --> 01:51.060
And if I uncomment the copy assignment operator, then we get the same error again.

01:53.950 --> 01:55.450
For some reason, it is displayed differently.

01:58.120 --> 02:00.700
Is this something which is useful to do?

02:01.330 --> 02:02.050
Yes, it is.

02:02.320 --> 02:07.330
In fact, the C++ standard library has several classes which are move-only.

02:08.200 --> 02:12.160
For example, fstream and iostream can be moved, but cannot be copied.

02:13.000 --> 02:18.460
There are some move-only classes in multi-threading, which we are not looking at in this course.

02:19.000 --> 02:23.800
And also the so-called "smart pointer" classes, which we will be looking at in the next section.

02:24.190 --> 02:25.840
Those are move-only as well.

02:29.070 --> 02:36.300
These types follow the RAII idiom, which means that only one object can own be given a resource instance at

02:36.300 --> 02:37.020
any one time.

02:37.920 --> 02:43.380
The object will acquire ownership of the resource in its constructor, and it will release the ownership

02:43.380 --> 02:44.400
in the destructor.

02:46.080 --> 02:52.830
We can also use move semantics to transfer the ownership of this resource, from one object to another.

02:56.780 --> 03:02.840
So if we look at fstream as an example, this has something called a "file handle" as the data member.

03:03.320 --> 03:08.390
So this is something that is used by the operating system, to communicate with the file on disk.

03:09.020 --> 03:11.150
The fstream constructor will open the file.

03:11.510 --> 03:13.970
It will be given a handle by the operating system.

03:14.960 --> 03:19.460
The destructor will close the file, so it will return to the handle back to the operating system.

03:21.710 --> 03:26.180
The fstream object cannot be copied, because that would mean there are two objects which are both

03:26.180 --> 03:29.840
going to try to close the file, which is not desirable.

03:30.470 --> 03:33.340
But we can move the object.

03:33.380 --> 03:37.430
So this will cause the file handle to be transferred, from one object to another.

03:38.330 --> 03:44.930
So the object which is moved from, no longer owns that file handle. It now has a null or invalid file

03:44.930 --> 03:50.450
handle. The object which is moved into becomes the owner of the file handle, and it is responsible

03:50.450 --> 03:54.680
for releasing that file handle when the file is no longer needed.

03:55.670 --> 03:58.820
So when the destructor is called for that object, it will close the file.

03:59.210 --> 04:03.080
Unless of course the file handle has been moved into another object in the meantime.

04:07.320 --> 04:14.550
There is one issue with move-only objects in C++11, which comes into effect if we try to use them

04:14.880 --> 04:15.960
in lambda expressions.

04:16.740 --> 04:22.410
If we have an fstream object, for example, and we want to capture this in a lambda expression,

04:23.460 --> 04:26.940
we cannot capture this by value, because that does not compile.

04:28.390 --> 04:33.940
I have now got a main() function which creates an object of this class, and a lambda expression which tries to

04:33.940 --> 04:34.930
capture it by value.

04:35.860 --> 04:37.030
So, what will happen here?

04:39.440 --> 04:44.900
And, right, it does not compile, because we tried to call the copy constructor, which is deleted.

04:45.200 --> 04:46.490
So it is the same problem again.

04:48.290 --> 04:51.650
We can capture the file stream by reference.

04:52.190 --> 04:56.780
And that will give us access to the file, but it does not give us the ownership.

04:57.230 --> 05:01.910
If we want to be able to transfer ownership into the lambda expression, then we need a capture

05:01.910 --> 05:02.420
by move.

05:02.750 --> 05:05.420
And that is not supported in C++11.

05:07.280 --> 05:10.100
However, it is supported in C++14.

05:11.780 --> 05:12.710
It is a little bit involved,

05:12.710 --> 05:20.090
though. C++14 has generalized lambda capture, so this means we can create lambda-local variables.

05:20.930 --> 05:24.980
You remember, we do this by declaring the variable inside the capture specifier.

05:25.460 --> 05:30.680
So this variable "lfs" is going to be a local variable to the lambda expression body.

05:30.890 --> 05:35.270
The type of this variable "lfs" will be deduced from its initializer.

05:35.270 --> 05:42.200
If we make this initializer an rvalue, then this is going to be initialized using the move constructor.

05:43.190 --> 05:52.610
So this will move the file handle, from this "fs" object in scope, into the local variable in the expression.

05:53.000 --> 05:55.310
So that is how you do capture by move.

05:57.560 --> 06:02.750
So in this expression, the "lfs" variable will be deduced as an fstream.

06:03.380 --> 06:09.290
The move constructor will be called to initialize it from the file stream argument, from the outer scope

06:09.300 --> 06:15.350
variable, and this variable in the lambda expression will take ownership of the file handle.

06:16.610 --> 06:20.240
And this was actually the second most requested feature in C++ 14.

06:20.900 --> 06:22.370
So obviously there is is a demand for it!

06:27.260 --> 06:28.820
So let's have an example of that.

06:29.270 --> 06:32.840
I am going to capture this local variable by move.

06:33.350 --> 06:35.900
So this is a vector of strings.

06:36.320 --> 06:39.680
I am also going to catch it by reference, so we can see what difference that makes.

06:40.430 --> 06:44.570
After each of these captures, we are going to print out the number of elements in the vector.

06:45.290 --> 06:50.990
I have put a pair of brackets after the lambda definition, so that will cause the lambda expression

06:51.050 --> 06:55.750
to be called. When the lambda expression captures by reference,

06:55.850 --> 07:01.280
it will have a reference to this vector of strings, but it does nothing to alter it.

07:01.490 --> 07:07.280
So we should expect to see 5 as the number of elements, when the lambda expression returns. With the

07:07.310 --> 07:08.600
capture by move,

07:08.960 --> 07:12.990
This scope variable is going to be moved into the lambda-local variable.

07:13.010 --> 07:18.290
So this is now the owner of those strings, and the original scope vector is going to be empty.

07:18.770 --> 07:23.060
So when the lambda returns, we expect to see 0 for the number of elements.

07:25.890 --> 07:32.010
And, what do we get? So we get 5 for capture by reference and 0 for capture by move.

07:33.060 --> 07:33.360
Okay.

07:33.360 --> 07:34.530
So that is it for this video.

07:34.980 --> 07:35.820
I will see you next time.

07:35.820 --> 07:38.180
But until then, keep coding!
