Hello again! In this video, we are going to look at lambda expressions in C++ 14.
So this is going to be the final video on lambda expressions.
We have already mentioned that, in C++14, the compiler can always deduce the return type of a lambda
expression.
It has to be the same in every branch.
So in effect, the return type of a lambda expression is auto. The compiler will provide the type information.
We can also use auto for the type of the arguments to the lambda expression.
So the compiler will deduce the type of the argument, depending on how the lambda expression is called.
If you have a lambda expression and you want to be able to call it with several different types, then
now you can just write the lambda expression once. As opposed to having to write one lambda expression
for every possible combination of types.
So this is rather like having a template function and in fact, it is implemented using templates.
Lambdas which take auto as argument are known as generic lambdas, or polymorphic
lambdas.
And this was actually the most requested feature in
C++14!
So when we put auto, as the argument type in the lambda, the compiler will deduce the type that the argument
needs to be. Just as it does with a template function.
And it uses the same rules. To write a generic lambda,
we just write
a lambda expression is normal, but we put auto as the type of the arguments.
So let's try this out.
We have our lambda expression, which has arguments of type auto.
We are going to call it several times, so we store in a variable.
If we call it with two arguments which are int, the compiler will deduce that x and y of type int. And
it will instantiate a suitable functor, and then call its function call operator. And for doubles and
strings.
And in fact, we can use any two types which can be added together.
So there we are, we just add the arguments together. And we can have different types. So if we make
this an int. (Better change the message as well!)
So it still works. The way this works is that the compiler will generate a functor class
which has a templated function call operator.
This is not actually correct, because x and y could be different types, so do not take this too literally!
And then when we call this, the compiler will put in some code, which creates a temporary object.
It will then look at the function arguments and instantiate the member function with the correct types
for X and Y. And then it will call this function call operator.
So there is quite a bit going on there.
This is just meant to show you what the compiler is doing.
If it is too confusing.
If it does not help you, then just ignore this.
The other main feature in C++ 14 is that we can create variables which are local to the lambda body.
We can do that in the capture specifier. If we put something like "y = 2".
This will create a variable y, which is local to the function body, and has the initial value 2.
So this is really another auto variable. We have to supply the initializer, so the compiler can work out
the type of "y".
We can also mix these with other captures, so we could have something like this. So we are capturing
all local variables by value, except for "v" which is captured by reference, and then we have this lambda
local variable.
So here is an example again. We have our lambda with the local variable "y", initial value 2.
When we call this lambda, the argument of the lambda, x, will have this value y added to it.
So when we call this lambda expression with argument 2, this argument 2 will have "y" added to it.
So we expect to get 4 and 7.
OK.
And we can also use a captured variable to initialize these lambda-local variables.
If we put "y = z + 1", and "z" is some variable in the enclosing scope, then this will
create a variable "y", which is local to the lambda body and has the initial value z +1.
We do not need to use any special syntax. "z" will be captured automatically. And this can get a
bit confusing,
but "z" is in the enclosing scope, so we can use it after the lambda is called. "y" is local to the lambda
body, so it only exists inside the lambda body.
So here we have that code.
We have the local variable "z".
We capture this in the lambda expression and use it to initialize our lambda-local variable, "y".
So "y" will actually have the value 2 again.
So we expect to get the same results. As we do.
And again, if you want to know how this is implemented, the compiler generates a functor class with a
private member, which is templated. And it is initialized in the constructor, using the code that is in
the capture expression.
And then the compiler will instantiate this functor with the correct type for the data member.
It will use the captured variable to initialize it. And then it will call the lambda expression with the
argument.
So this is getting quite intricate now.
So you may be wondering what the point of all this is.
You can already create local variables inside lambda expression bodies, just like you can with a
normal function.
And you can also capture variables and use them to initialize the local variables.
The reason is, this allows you to do capture by move. And we will find out why that is useful when we
look at move semantics.
But it is useful enough that it was the second most requested feature in
C++14!
So there you go!
Okay, so that is it for this feature.
I will see you next time.
Until then, keep coding!