In the last video we learned about stateless lambda expressions.
That is, lambda expressions that capture no information from their environment.
In this video, we'll learn about their close relative, the stateful lambda expression.
That is a lambda expression that does capture information from its environment.
It's important to note that stateful lambda expressions are just like stateless lambda expressions,
except they have non empty capture lists.
So everything we do with the stateless lambda, we can do with a stateful lambda and more.
So what exactly do we mean when we refer to a lambda capturing information from its environment and
how is it able to do this?
Well, what we're referring to is the lambda ability to use variables declared within its reaching scope
without having to pass them into the lambda as parameters.
This is accomplished by simply listing the variables we would like the lambda to use within its capture
list.
Pretty cool, right?
But how does it work?
Well, to fully understand this, we first need to take a look under the hood to see what happens when
a lambda expression is compiled.
Here we can see a simple stateless lambda expression defined to represent the lambda.
The compiler generates what's known as a closure type class with a default constructor and an operator
function.
Implementing the logic of the lambda.
The class isn't generated until compile time, so it might now be clear why the auto keyword must be
used is to specify the for the type and lambdas.
It's because when we define lambda expressions, the type hasn't been generated yet.
The type is only generated when the lambda is compiled and so of course can only be assigned by the
compiler at compile time.
With this in mind, can you see what's happening when we assign a land expression to a variable?
What we're actually doing is instantiating an object of the compiler generated closure by making a call
to its constructor.
Hopefully now you'll have a better understanding of how lambdas operate behind the scenes.
But that still leaves the question of how they can capture information from their environment using
the capsule list as the challenge.
Positive video now and see if you can figure it out with your new knowledge of how lambdas work when
you're done.
Unpause the video and we'll go through it in the next slide.
If you thought of storing the captured variables as member variables of the compiler generated closure,
then you were absolutely right.
When defining a stateful lambdas capture list where defining the parameterized constructor of the lambdas
compiler generated closure when the state for Lambda Expression is instantiated.
A unique function object is created using the parameterized constructor, which stores the captured
variables as member variables.
We can see this being done in the example.
A simple stateful lambda expression is defined which captures the local variable Y and takes a single
parameter x.
The compiler generates a closure for the lambda with a parameterized constructor that takes as its parameter
the variable y and assigns it to the closures member variable y.
You'll notice that the generated closure has an operator function implementing the logic of the lambda
as we would expect.
However, this time it's a constant member function.
This means that no member variable of the instantiated object can be modified by this function, and
so the member variable y cannot be modified.
The reason for this is because by default, all variables captured by value are captured by contract
value.
As you can imagine, this can be quite restricting when we're trying to implement certain functionalities.
So C++ provides us with a multitude of ways in which we could capture variables.
Let's take a look at them.
As seen in the last example.
The first way in which a lambda expression can capture a variable is by value.
This is the default capturing mode, and so it can be accomplished by simply listing the variables.
Name in the lambdas capsule list.
It's important to remember that a variable being captured by value is actually being captured by cost
value, so it won't be modifiable within the lambda.
But what if we want to capture a variable by value and modify it within the lambda?
In that case, we can use the keyword mutable to tell the compiler to generate the lambdas operator
function as a non const member function.
By doing so, we can capture variables by value and modify them within the lambda.
As you might have guessed, we can also capture variables by reference.
In this case, any changes made to a variable captured by reference within the lambda will be visible
outside the lambda.
Using both capturing modes, we can define a lambdas capsule list any number of ways for any number
of variables.
But what if we want to capture a large number of variables, but we don't want to have to list each
variable.
How do we do this?
This is where default captures come in.
A default capture allows the lambda to capture all variables referenced within its body according to
the defined capture mode.
An equal sign positioned first in the lambdas capsule list denotes that all referenced variables should
be captured by value.
An ampersand denotes that they should be captured by reference.
And if we use the this keyword, this denotes that the referenced member variables of the current object,
the lambda is defined within should be captured by reference.
When implementing default captures, we can also explicitly capture variables according to different
capture modes.
It's important to note that when we do so, we must always place the default capture first in the capture
list.
Also, the explicit capture cannot be the same as the default.
Otherwise the lender won't compile.
With so many possible combinations of default and explicit parameters, it's hard to cover them all.
But what we've covered should prepare you for most situations in which you will need to use stateful
lambdas.
Now let's head over to the IDE and implement some.