WEBVTT

00:00.210 --> 00:07.710
Hello again! In this video, we are going to look at lambda expressions and capture. We can pass

00:07.710 --> 00:10.080
data to a lambda expression through the arguments.

00:10.470 --> 00:16.770
If we have an algorithm call which uses a lambda expression, then we can only pass elements of the container

00:16.770 --> 00:18.270
that the algorithm is processing.

00:19.020 --> 00:25.800
We cannot add more arguments because the signature of the lambda expression has to match the expectations

00:25.800 --> 00:26.730
of the algorithm.

00:28.260 --> 00:29.240
So what can we do?

00:29.580 --> 00:35.610
A lambda expression has access to global or non-local variables, just like a regular function.

00:36.420 --> 00:39.480
It can also access static variables in the same scope.

00:40.890 --> 00:44.110
There is some very limited access to local variables.

00:44.130 --> 00:46.740
So this is in the scope where the algorithm is being called.

00:49.680 --> 00:56.550
If we have a local variable, which is a reference and was initialized with a const expression, then

00:56.550 --> 00:59.100
the lambda expression can read and write those variables.

00:59.730 --> 01:05.460
If we have a local variable, which is an integer or enumerated type, and was initialized with a constant

01:05.460 --> 01:10.320
expression, then the lambda expression can read those, but not modify them.

01:11.880 --> 01:15.210
So that is pretty limited and also not very well supported.

01:17.600 --> 01:24.350
So we have a lambda expression, which is just going to print out some variables in its body.

01:25.130 --> 01:30.890
We have a global variable (Move myself out of the way. Going down.)

01:34.230 --> 01:36.780
So we have a global variable, which it can access.

01:37.290 --> 01:41.070
We have a static variable in the same scope, which it can access.

01:42.150 --> 01:47.370
We have a constant integer which it should be able to access, but this does not compile with Visual

01:47.370 --> 01:48.150
C++.

01:51.700 --> 01:53.710
"Cannot be implicitly captured"

01:55.210 --> 01:58.630
And the other possibility is a reference initialized with a constant.

01:59.080 --> 02:01.600
But this is not supported by any compiler that I have.

02:04.210 --> 02:05.090
Same error, again.

02:06.680 --> 02:07.640
So what can we do?

02:08.780 --> 02:10.850
Clearly, we need to do something special.

02:11.120 --> 02:14.000
And fortunately, the language does provide something special.

02:14.300 --> 02:20.420
We can "capture" the local variables. And, to do that, we just put the name of the local variable inside the

02:20.420 --> 02:21.230
square brackets.

02:21.650 --> 02:27.080
So when we define a lambda function, we are now defining a lambda function, which will capture this

02:27.080 --> 02:28.340
local variable, "n".

02:28.970 --> 02:35.810
And then the variable is available inside the function body, so we can just use it for whatever purposes

02:35.810 --> 02:36.260
we want.

02:37.940 --> 02:41.270
If we have more than one, we can just capture both, and put commas between them.

02:41.660 --> 02:47.300
As an example of this, the find_if() algorithm we have seen before. We pass the iterator range and a lambda

02:47.300 --> 02:47.810
expression.

02:48.530 --> 02:54.230
And this one will find the first element which has more than 5 characters in the string.

02:55.550 --> 03:01.760
So here is that code. We have a vector of strings, and we are going to find the first one which has more

03:01.760 --> 03:02.630
than 5 characters.

03:04.430 --> 03:05.990
And the first one is, "collection".

03:06.770 --> 03:11.660
But what happens if we want some different value from 5, or we want to be able to change this?

03:12.410 --> 03:18.710
We can use a lambda with capture. So we can have a local variable, "n", which has the number of elements we

03:18.710 --> 03:19.610
want to look for.

03:20.210 --> 03:24.680
And then we can capture this local variable "n" and use that in the lambda expression body.

03:25.070 --> 03:29.360
So this is going to find the first element in the vector which has more than "n" characters.

03:30.780 --> 03:31.450
That is, "collection".

03:31.460 --> 03:38.420
So if we change "n" to... actually we need to move - again, that is not a very good choice of data.

03:42.290 --> 03:44.270
So if we make - so let's try that again.

03:46.150 --> 03:47.290
So it is still collection.

03:47.710 --> 03:49.690
If we make this, 3.

03:53.550 --> 03:54.870
Then we get "words".

03:57.280 --> 03:59.050
So how is this implemented?

04:01.330 --> 04:02.800
(Just get myself back in the picture!)

04:03.760 --> 04:05.290
So how is this implemented?

04:05.680 --> 04:09.520
A lambda, which has capture, is implemented as a functor with state.

04:10.030 --> 04:15.700
So this means the compiler will generate a class which has a private data member, and that data member

04:15.910 --> 04:17.920
will store the captured variable.

04:19.420 --> 04:25.450
This member will be initialized in the constructor of the class. And then that private data member is

04:25.450 --> 04:27.790
available in the body of the member function.

04:28.270 --> 04:29.680
The function call operator.

04:32.280 --> 04:34.260
By default, this is all done by value.

04:34.800 --> 04:40.050
So the captured variable is passed to the function's constructor by value, and the data member is going

04:40.050 --> 04:41.370
to be a value type.

04:42.030 --> 04:48.510
So this means that the functor object will have its own copy of the captured variable. And that is known

04:48.510 --> 04:49.830
as capture by value.

04:51.360 --> 04:54.120
By default, this statement member will also be const.

04:54.420 --> 05:01.480
So this means that the object has a const copy of the variable, and the function call operator cannot modify

05:01.480 --> 05:01.660
it.

05:02.370 --> 05:05.580
So this is what the equivalent code with a functor would look like.

05:05.880 --> 05:10.110
So this is the code that the compiler will generate for the previous example.

05:10.500 --> 05:18.600
So we have a class which has a private member with the captured variable. A const copy of the variable.

05:19.050 --> 05:24.300
This is initialized in the constructor. And then, in the function call operator,

05:24.300 --> 05:27.690
we have the code from the lambda expression body.

05:29.820 --> 05:34.620
And then when we call the algorithm, the compiler will put in some code, which creates an object of

05:34.620 --> 05:38.250
this class, and passes the variable "n" to the constructor.

05:42.910 --> 05:43.840
And there we are.

05:46.040 --> 05:49.160
And you can experiment with changing the words around if you like.

05:51.090 --> 05:58.620
Let's say now that we want to be able to find the index of this variable that matches. So we have another

05:58.620 --> 05:59.370
local variable.

06:00.450 --> 06:06.090
And then every time the algorithm processes an element, we increment this variable.

06:07.500 --> 06:12.210
So if we set this to minus one, that will be equal to the number of processed elements minus one,

06:12.210 --> 06:13.800
which will give us the index.

06:15.920 --> 06:17.750
OK, so what do you think will happen?

06:21.470 --> 06:27.260
Well, we get a compiler error, and the reason for that is that, this will generate a functor which

06:27.260 --> 06:29.720
has a const member to represent the index.

06:30.350 --> 06:36.350
So in the member function, this will increment a const private data member and that is not allowed.

06:37.460 --> 06:42.680
What we can do is add the "mutable" the keyword after the lambda expression signature.

06:43.040 --> 06:48.170
So that means that the data member is not going to be const. It is going to be a modifiable variable.

06:48.860 --> 06:53.330
And then this will compile and then we get the value minus one.

06:54.620 --> 06:59.240
And the reason for that is that the functor has a copy of the captured variable.

06:59.720 --> 07:06.050
So when this was being modified in the functor's member function, that was only modifying the copy.

07:06.230 --> 07:08.900
It was not doing anything to change this local variable.

07:09.680 --> 07:15.470
And then when we print this out, this is going to be the local variable and not the copy which the

07:15.680 --> 07:17.960
functor made, which disappears when this call returns.

07:20.120 --> 07:25.760
OK, so we need to find out now how to be able to modify captured variables, and we will do that in the

07:25.760 --> 07:28.970
next video. But until then, keep coding!
