WEBVTT

00:00.180 --> 00:05.030
Hello again! In this video, we are going to continue looking at lambda expressions and capture.

00:06.960 --> 00:13.320
We saw in the last video that a lambda expression can capture a local variable by a value. That gives it

00:13.320 --> 00:16.170
a copy of the local variable, which is const.

00:16.170 --> 00:19.240
And we can use the mutable word if we want to modify

00:19.260 --> 00:25.680
that. If we want to be able to modify the captured variable, then we need to capture it by reference.

00:26.310 --> 00:30.450
And we can do that by putting an ampersand before the variable name inside the square brackets.

00:31.020 --> 00:38.160
So this is going to capture the variable "n" by reference and then the "n" inside the lambda expression

00:38.160 --> 00:41.400
body is going to be a reference to this local variable.

00:42.540 --> 00:48.470
So if the lambda expression makes any changes to this variable, then these will also affect the variable

00:48.510 --> 00:49.530
in the local scope.

00:52.140 --> 00:59.100
So going back to the exercise in the last video, we were trying to find a word in this vector, which

00:59.100 --> 01:03.630
has more than 5 characters, and we also wanted the index, But we had a problem, because we could

01:03.630 --> 01:06.540
only modify a copy of the index variable.

01:06.870 --> 01:13.920
So that gave the wrong answer. If we now capture it by reference, so we are going to capture this local

01:13.920 --> 01:15.600
variable "idx" by reference.

01:16.740 --> 01:19.710
This is going to be a reference in the lambda body.

01:20.070 --> 01:25.380
So when we increment it here, this is going to also increment the local variable through the reference.

01:26.220 --> 01:30.450
And then, when the find_if() call returns, this should have the correct value.

01:31.170 --> 01:32.370
So let's see if it works.

01:34.450 --> 01:41.050
And there it is. So we do get the correct value. A lambda which does capture it by reference, is going

01:41.050 --> 01:45.040
to be implemented as a functor with state, similar to the capture by value.

01:45.640 --> 01:51.250
So the compiler will generate the definition of a class with a function call operator, and a private

01:51.370 --> 01:53.230
data member, to store the captured value.

01:53.950 --> 02:00.400
This time, the private member is going to be a reference to the captured variable, and the constructor

02:00.400 --> 02:00.940
will take

02:00.940 --> 02:03.010
that argument by reference.

02:05.810 --> 02:11.360
Then the function call operator can modify the captured variable through that reference. And that

02:11.360 --> 02:19.700
is "capture by reference". The functor that the compiler would generate in the last example is going

02:19.700 --> 02:20.660
to look something like this.

02:21.200 --> 02:26.930
So we have two private members, one for the variable "n", which is captured by value, and one for the

02:26.930 --> 02:31.520
"idx" variable, which is captured by reference. And then the [constructor]

02:31.520 --> 02:36.410
will initialize these. And then we have the function call operator.

02:38.430 --> 02:44.460
And then inside the algorithm call, the compiler will add code, which will create an object of this

02:44.460 --> 02:47.880
class. And it will pass the local variables as arguments.

02:51.180 --> 02:52.740
And that does give the same result.

02:53.850 --> 02:59.040
It is also possible for a lambda expression to capture all the variables which are in scope. That is known

02:59.040 --> 03:03.450
as an "implicit capture". And we can do that by value, or by reference.

03:04.140 --> 03:10.070
If we put an equals sign inside the square brackets, that will capture all the variable by value. And an

03:10.080 --> 03:14.490
ampersand will catch them all by reference, which is possibly not the best thing to do.

03:15.810 --> 03:21.660
The reason is, if you capture all the local variables by reference, then the body of the lambda expression

03:21.660 --> 03:24.300
is allowed to modify any variable which is in scope.

03:25.050 --> 03:29.970
And if you have a program which gets modified a lot, that can be a bit of a maintenance headache.

03:30.600 --> 03:36.060
So the best thing to do is to capture variables by reference, only if you actually need to alter them in

03:36.090 --> 03:36.840
the lambda expression.

03:38.400 --> 03:39.120
And you can do that.

03:39.120 --> 03:44.940
For example, if you only want to modify x, then you can put equals, comma, ampersand x.

03:45.540 --> 03:49.320
That will capture x by reference and all the other variables by value.

03:51.330 --> 03:55.230
Alternatively, if you have some variables that you want to protect; you want to stop them from being

03:55.230 --> 03:56.520
altered by the lambda expression.

03:57.030 --> 04:01.860
You can capture those by value, and then implicitly capture everything else by reference.

04:03.800 --> 04:07.790
It is possible to define a lambda expression inside a member function.

04:08.120 --> 04:10.550
So this is different now from the algorithm case.

04:10.940 --> 04:17.210
In the algorithm, the lambda was a callable object, which we were supplying, and the algorithm function

04:17.210 --> 04:18.350
called that directly.

04:19.880 --> 04:24.770
We can just write a lambda expression inside a member function, and that is some local code. And we can

04:25.040 --> 04:27.950
execute that, or we can save it and use it later.

04:30.220 --> 04:36.430
When we have a member function, the member function is always called on some object, and a pointer

04:36.430 --> 04:40.150
to that object is passed as the first argument to the function.

04:40.210 --> 04:44.680
So this is actually an extra argument that you do not see in the signature.

04:45.610 --> 04:50.770
If we call a member function on an object, then the address of the object is passed as the first argument,

04:51.250 --> 04:57.430
and you can access it inside the body of the member function, as a varaible called "this". And "this" will be

04:57.430 --> 04:58.810
a pointer to a Test.

05:01.260 --> 05:06.300
So this means that "this" is, in effect, a local variable, which is in scope in the member function,

05:06.810 --> 05:09.300
and perhaps we can capture that in a lambda expression.

05:09.930 --> 05:11.070
And yes, we can.

05:11.880 --> 05:18.330
A lambda expression is allowed to capture "this", and that means that the expression can access these data

05:18.370 --> 05:23.380
members of the class. All members, including the private ones. And it can also call other member

05:23.430 --> 05:24.420
functions of the class.

05:26.370 --> 05:30.000
The syntax for doing that is to put "this" inside square brackets.

05:30.750 --> 05:37.990
If you do an implicit capture, by reference or by value, that will also capture "this". You are not allowed

05:38.010 --> 05:39.900
to put equals "this" or ampersand "this".

05:41.070 --> 05:46.440
There is a slight peculiarity, in that all these forms will capture the object by reference, even though

05:46.440 --> 05:49.800
it looks as though they should, in some cases, capture it by value.

05:50.310 --> 05:51.540
So let's have an example of this.

05:53.630 --> 05:55.130
So here is our class.

05:55.850 --> 06:00.560
We have a member function, which has a lambda expression defined in it. We will look at this in a

06:00.560 --> 06:00.890
minute.

06:02.600 --> 06:04.970
The class has a private data member.

06:05.450 --> 06:12.110
We initialize this in-place, with the value 10 and then, in the member function, we define our lambda expression.

06:12.800 --> 06:14.610
So this just defines the lambda.

06:14.630 --> 06:18.740
So this will cause the compiler to generate the class definition.

06:19.910 --> 06:25.220
And then when we put brackets after it, this will actually cause the code to be executed.

06:25.790 --> 06:31.310
So this will make the compiler create an object of this class and call the function call operator.

06:32.780 --> 06:38.480
So this will cause the compiler to create an object of the functor class. Not this class, the functor

06:38.480 --> 06:42.830
class! And call the function call the operator in the functor class.

06:43.580 --> 06:48.290
So we have actually got two classes in here: the Test class, which we defined, and the functor, which

06:48.290 --> 06:49.460
the compiler will define.

06:51.840 --> 06:58.410
And then, inside the lambda expression, we capture "this", so we can access the private data of the Test class.

06:59.160 --> 07:04.560
So we can check the value of the time member. And because "this" is actually captured by reference,

07:04.560 --> 07:09.210
even though it looks as though it has been captured by value, we can also modify this private data member.

07:11.830 --> 07:14.350
So this is a bit of a silly example, really,

07:14.740 --> 07:21.400
I could not think of anything more sensible. So we are going to look at this member. If it is greater than

07:21.410 --> 07:24.640
0, we print out the value. If it is equal to 0,

07:24.670 --> 07:29.080
we print "Liftoff!" and then we [decrement] it in either event.

07:30.400 --> 07:33.700
If the time is less than 0, we do not do anything.

07:33.700 --> 07:37.180
We just decrement it. In the main() function.

07:37.180 --> 07:41.230
We create an object of this class, and then we call the countdown member function, 12 times.

07:42.400 --> 07:44.410
So, let's see what happens.

07:47.090 --> 07:51.620
So we get 10, 9, 8, blablabla, lift off and then nothing after that.

07:53.270 --> 07:55.940
So the first time through, "time" is 10.

07:56.720 --> 08:00.380
So it prints out the time and then decrements it. Then the next time,

08:00.410 --> 08:06.640
"time" is 9 and so on. When "time" is 0, lift off, decrement. Then "time" is less than 0,

08:06.650 --> 08:08.660
so just decrement.

08:10.640 --> 08:15.860
You may think that capturing "this" by reference is a little bit dangerous, because the lambda expression

08:15.860 --> 08:23.540
can modify any data member. And in C++ 17, we also have the opportunity to capture the object by value.

08:24.260 --> 08:29.210
So if we put star this inside the square brackets, then that captures the object by value.

08:29.900 --> 08:34.130
So that means the lambda expression only has a copy of the object.

08:34.640 --> 08:36.740
And by default, "this" will be immutable.

08:37.370 --> 08:40.100
So this means that the lambda expression cannot change the object.

08:40.550 --> 08:45.650
So if we have the same code again and we capture "this" by value, then this should not compile.

08:51.180 --> 08:51.690
Guess what?

08:52.380 --> 08:56.020
This is not supported in visual C++. Or at least, not this version.

08:56.040 --> 08:57.260
This is possibly a year old,

08:57.270 --> 08:59.000
but it is not ancient.

08:59.310 --> 09:07.290
So I am going to compile this in C++ 17 mode, which GCC does support. And we get a compiler error. So,

09:07.290 --> 09:09.000
we are not allowed to modify "this".

09:10.050 --> 09:11.550
So the object is read only.

09:11.760 --> 09:13.980
So we have a const copy.

09:15.390 --> 09:18.360
If I change this to mutable.

09:23.130 --> 09:24.390
And then, let's try that again.

09:25.440 --> 09:26.400
So it does compile now.

09:26.940 --> 09:31.710
But if we run it, then we get 10.

09:32.760 --> 09:35.760
And it is the same problem we had with the index in the last video.

09:36.660 --> 09:41.340
So when we call this member function, the member function has a copy of the test object and

09:41.340 --> 09:45.030
it modifies that copy, but not the actual object in main().

09:46.440 --> 09:48.070
Okay, so that is it for this video.

09:48.540 --> 09:51.510
I will see you next time, but until then, keep coding!
