WEBVTT

00:00.240 --> 00:05.160
Hello again! In this video, we are going to look at lambda expressions in C++ 14.

00:05.580 --> 00:08.460
So this is going to be the final video on lambda expressions.

00:09.600 --> 00:15.510
We have already mentioned that, in C++14, the compiler can always deduce the return type of a lambda

00:15.510 --> 00:16.050
expression.

00:16.770 --> 00:18.390
It has to be the same in every branch.

00:19.140 --> 00:25.890
So in effect, the return type of a lambda expression is auto. The compiler will provide the type information.

00:28.300 --> 00:32.260
We can also use auto for the type of the arguments to the lambda expression.

00:32.710 --> 00:37.660
So the compiler will deduce the type of the argument, depending on how the lambda expression is called.

00:38.320 --> 00:43.450
If you have a lambda expression and you want to be able to call it with several different types, then

00:43.450 --> 00:49.000
now you can just write the lambda expression once. As opposed to having to write one lambda expression

00:49.000 --> 00:50.980
for every possible combination of types.

00:51.820 --> 00:56.650
So this is rather like having a template function and in fact, it is implemented using templates.

00:58.460 --> 01:03.370
Lambdas which take auto as argument are known as  generic lambdas, or polymorphic

01:03.370 --> 01:03.640
lambdas.

01:04.210 --> 01:06.850
And this was actually the most requested feature in

01:06.850 --> 01:07.360
C++14!

01:09.500 --> 01:15.860
So when we put auto, as the argument type in the lambda, the compiler will deduce the type that the argument

01:15.860 --> 01:18.590
needs to be. Just as it does with a template function.

01:19.130 --> 01:24.080
And it uses the same rules. To write a generic lambda,

01:24.080 --> 01:25.360
we just write

01:25.370 --> 01:28.700
a lambda expression is normal, but we put auto as the type of the arguments.

01:30.410 --> 01:31.520
So let's try this out.

01:31.940 --> 01:36.020
We have our lambda expression, which has arguments of type auto.

01:36.950 --> 01:39.920
We are going to call it several times, so we store in a variable.

01:42.520 --> 01:49.330
If we call it with two arguments which are int, the compiler will deduce that x and y of type int. And

01:49.330 --> 01:56.160
it will instantiate a suitable functor, and then call its function call operator. And for doubles and

01:56.170 --> 01:56.680
strings.

01:57.010 --> 01:59.770
And in fact, we can use any two types which can be added together.

02:02.500 --> 02:07.390
So there we are, we just add the arguments together. And we can have different types. So if we make

02:07.390 --> 02:11.500
this an int. (Better change the message as well!)

02:14.270 --> 02:19.640
So it still works. The way this works is that the compiler will generate a functor class

02:20.180 --> 02:23.780
which has a templated function call operator.

02:24.830 --> 02:29.720
This is not actually correct, because x and y could be different types, so do not take this too literally!

02:32.280 --> 02:38.400
And then when we call this, the compiler will put in some code, which creates a temporary object.

02:39.270 --> 02:44.610
It will then look at the function arguments and instantiate the member function with the correct types

02:44.610 --> 02:49.140
for X and Y. And then it will call this function call operator.

02:50.010 --> 02:51.420
So there is quite a bit going on there.

02:52.020 --> 02:54.780
This is just meant to show you what the compiler is doing.

02:54.780 --> 02:56.150
If it is too confusing.

02:56.160 --> 02:58.230
If it does not help you, then just ignore this.

02:59.550 --> 03:05.640
The other main feature in C++ 14 is that we can create variables which are local to the lambda body.

03:06.210 --> 03:10.860
We can do that in the capture specifier. If we put something like "y = 2".

03:11.340 --> 03:16.740
This will create a variable y, which is local to the function body, and has the initial value 2.

03:19.370 --> 03:25.390
So this is really another auto variable. We have to supply the initializer, so the compiler can work out

03:25.400 --> 03:26.390
the type of "y".

03:28.850 --> 03:33.350
We can also mix these with other captures, so we could have something like this. So we are capturing

03:33.350 --> 03:39.710
all local variables by value, except for "v" which is captured by reference, and then we have this lambda

03:39.710 --> 03:40.460
local variable.

03:41.900 --> 03:47.150
So here is an example again. We have our lambda with the local variable "y", initial value 2.

03:48.140 --> 03:54.290
When we call this lambda, the argument of the lambda, x, will have this value y added to it.

03:55.610 --> 04:01.490
So when we call this lambda expression with argument 2, this argument 2 will have "y" added to it.

04:01.970 --> 04:03.680
So we expect to get 4 and 7.

04:07.100 --> 04:07.490
OK.

04:08.540 --> 04:12.710
And we can also use a captured variable to initialize these lambda-local variables.

04:13.430 --> 04:20.330
If we put "y = z + 1", and "z" is some variable in the enclosing scope, then this will

04:20.690 --> 04:26.570
create a variable "y", which is local to the lambda body and has the initial value z +1.

04:28.220 --> 04:34.670
We do not need to use any special syntax. "z" will be captured automatically. And this can get a

04:34.670 --> 04:35.270
bit confusing,

04:35.270 --> 04:41.540
but "z" is in the enclosing scope, so we can use it after the lambda is called. "y" is local to the lambda

04:41.540 --> 04:45.020
body, so it only exists inside the lambda body.

04:45.590 --> 04:47.030
So here we have that code.

04:47.030 --> 04:48.410
We have the local variable "z".

04:48.890 --> 04:54.410
We capture this in the lambda expression and use it to initialize our lambda-local variable, "y".

04:55.340 --> 04:57.260
So "y" will actually have the value 2 again.

04:57.560 --> 05:01.370
So we expect to get the same results. As we do.

05:02.720 --> 05:08.120
And again, if you want to know how this is implemented, the compiler generates a functor class with a

05:08.120 --> 05:13.940
private member, which is templated. And it is initialized in the constructor, using the code that is in

05:13.940 --> 05:14.870
the capture expression.

05:17.610 --> 05:23.770
And then the compiler will instantiate this functor with the correct type for the data member.

05:24.660 --> 05:29.820
It will use the captured variable to initialize it. And then it will call the lambda expression with the

05:29.820 --> 05:30.300
argument.

05:30.840 --> 05:32.970
So this is getting quite intricate now.

05:34.440 --> 05:36.630
So you may be wondering what the point of all this is.

05:36.930 --> 05:41.850
You can already create local variables inside lambda expression bodies, just like you can with a

05:41.850 --> 05:42.570
normal function.

05:43.080 --> 05:47.040
And you can also capture variables and use them to initialize the local variables.

05:47.790 --> 05:52.890
The reason is, this allows you to do capture by move. And we will find out why that is useful when we

05:52.890 --> 05:53.850
look at move semantics.

05:54.480 --> 05:57.840
But it is useful enough that it was the second most requested feature in

05:57.840 --> 05:58.290
C++14!

05:59.520 --> 06:00.300
So there you go!

06:01.260 --> 06:02.940
Okay, so that is it for this feature.

06:03.330 --> 06:04.200
I will see you next time.

06:04.470 --> 06:06.330
Until then, keep coding!
