(upbeat music)
Frank: Hello, everyone. Welcome back.
In this video, I'm gonna talk about stateful Lambdas
and I'm gonna show you some examples in the IDE.
I'm in the section 21 workspace, as you can see right here,
and I'm in the stateful Lambdas project.
So I've got 10 different test functions
that I'm gonna show you.
Each one does something just a little bit differently
and the idea is to give you a flavor
for what stateful Lambdas can do
and different ways that they can do things.
So let's go over this first test example.
I've already gone ahead and built this
and run it in all test cases over here running
so I'm just gonna walk you right through them.
I'm including iostream, vector, and algorithm
which we'll use later.
And let me just scroll up just a little bit here.
All right, so notice right here
that we have a variable called global_x
and I've initialized it to 1,000.
That variable is global to this file
so it's accessible anywhere in this file.
However, I can't capture that kind of variable, okay?
We can't capture those kinds of variables.
We can't capture global variables, for example.
We can't capture static variables.
So if you try to capture that variable
in the capture list right here,
and I'll talk about that in a minute,
the compiler is gonna give you a warning
about only automatic variables can be captured.
And that's all about the reaching scope of the Lambda.
That's what can the Lambda see.
So let me clear this
and let's go over this one step at a time.
So remember that we've got that global_x out there
and it's 1,000.
Now inside this test function,
I have a local variable called x, right?
It's called local_x and it's set to 100.
Now I've got my Lambda expression
which I'm assigning to this variable l.
And notice it's got a capture, right?
Those square brackets, that's where we put all our captures.
So this makes this guy a stateful lambda,
not a stateless lambda.
In this case, we're capturing local_x.
That's that guy right there.
Now, by default, we are capturing by value.
That's the default capture mode.
What that means is that this Lambda
is going to have a copy of local_x in it, okay?
So let's just called this guy local_x
and it is going to be a copy of what it captured.
So it's gonna have 100 in it.
Let me write that really large so there's no confusion.
This is a copy.
In this case, I'm making a copy of that integer.
If this variable here were to be an object,
then we're gonna call a copy constructor to make a copy.
So it's really important to understand
that we're making a copy.
Then we're just outputting the local_x's value
and the global_x's value.
So now when we call that Lambda,
what's going to happen is this code right here will execute,
the local_x will display.
This local_x, that's the copy,
and it's gotta display 100.
And you can see right up here my Test1,
it's gonna display 100
and then we're gonna display global_x
and it's just finding that
just because of normal scope rules.
It's gonna find global_x, it's a 1,000,
and it's gonna display 1,000 right here.
Pretty straightforward.
Now, if we try to modify local_x inside the Lambda's body,
we're not gonna be able to.
So in order to do that, we need to use the mutable keyword.
And I'm gonna do that in the next example.
So let me scroll up just a little bit.
I'm gonna scroll here to Test2.
I'm gonna scroll up here as well.
So right now, we're talking about Test2,
which is that output right over here.
Okay, so let's take a look at Test2.
Test2, we have a local variable x, and here's our lambda.
We're capturing that local variable x,
so that means that this lambda has an x
and it's a copy of this x,
so I'm gonna put 100 in here.
Okay, that's really important.
Now, when we execute the body,
notice we're changing that x,
we're incrementing it by 100,
it is allowed now because I'm using mutable.
Because I have that mutable keyword,
it's allowing me to do that.
So what's gonna happen here
is I'm going to increment the local value,
not this guy, the local, the copy,
I'm gonna increment it by 100.
So this guy now becomes 200, and I'm gonna display that.
So when I call the Lambda, this body executes,
it just incremented that to 200
and we're gonna display x which is 200 right there.
Now when I leave, remember I just called that Lambda here,
now I'm back and I'm gonna display x.
Well, the only x that this statement
has access to is this x,
and that x has not been changed because we copied it.
Remember, this was a copy here.
That was a copy.
So in this case, it hasn't changed,
so what's gonna display is 100.
Now, this is the piece that's really important to understand
and a lot of students and a lot of people in general
don't understand how this works with Lambdas,
but it's really important to understand.
Notice what happens here.
I'm calling Lambda again, I'm calling that Lambda l again.
So I'm gonna call this Lambda again.
What happens is the body of the Lambda executes,
we do not capture again.
The capture only happened here
when we initialize that variable l.
So we do not capture the 100 again.
That Lambda expression already has that local x
and it's right here and it's 200.
All right, so again, we're right here.
I'm calling this Lambda.
That's gonna increment this guy now to 300
and it's gonna display 300 right there.
Now when I come back here, I display x,
which is this x that's local to this Test2 function
and it's gonna display 100.
Hopefully that makes sense.
If it doesn't make sense,
go back to the slides and look at the slides.
And it's really important
that you understand what's happening.
Remember, that's the stateful Lambda.
When we have that lambda,
remember that class that got created.
Remember we talked about that closure class
that was created?
It has a constructor and it has the function call operator.
This would be the constructor.
Right here, the constructor will be called.
Right here, the operator function will be called.
Right here, the operator,
the function call operator will be called.
That's really important to understand.
The only time that we capture
is when that constructor's executed
and that's only gonna be executed once.
Remember, we can only initialize an object once.
And that's exactly what l is. It's an object.
So again, take your time with that,
wrap your head around it
'cause it's really, really important to understand
what's happening there.
Okay, so let's take a look at Test3.
Now what's happening with Test3 is
we're capturing by reference.
So again, I've got my x right here, it's 100.
Now we're capturing that x by reference
and that's the syntax right there.
We're using that ampersand, the referencing operator.
So what happens now is, and it's mutable as well,
what we're gonna do now is that x is local
and it's not really an x, right?
We have a reference.
That's really important to understand as well.
So whenever we change x,
we're actually changing the actual x right here.
So what do we do here?
The body increments x by 100 and we display x.
So let's call it.
What's gonna happen?
It's already captured x, right?
Its value is a reference to the actual.
We're gonna increment it by 100.
What we're gonna do is we're gonna increment
not any local copy.
There is none.
We're gonna increment this guy by 100
and we're gonna display it.
So that's going to display 200 here
and you can see it right up there.
Now, when we return from the function call,
we're going to display this x right here.
It has been changed because we captured by reference.
So that's also going to display 200.
Okay, so that's three examples
that are a little bit different from one another,
but hopefully you'll see what's going on.
All right, so now in this case,
we're gonna use default capture by value
and we're gonna do it mutable just to make it more fun
so we can actually change something
and you can really see what's happening.
So here we're in Test4
and we've got these three local variables
to this Test4 function, x, y, and z, 100, 200, 300.
Here's our Lambda variable
and we are capturing everything by value.
Okay, so that's the default capture by value.
So we're capturing x, y, and z by value.
What's gonna happen here is I'm gonna make a copy of x,
I'm gonna make a copy of y, and I'm gonna make a copy of z.
So x is 100, so I'm gonna make that copy,
y is 200
and z is 300.
But now notice something,
we are not using z in this code at all.
So the compiler's not gonna capture it.
Why capture something that you're not going to use?
So this won't happen.
It's not gonna waste its time doing that.
So the only thing that's captured is x and y.
And you're gonna get a warning in the output
when you build this program saying
that variable z was not captured since it's not being used.
Some kind of unused variable warning.
So again, by value, right?
So again, we've made copies.
Now I'm going to increment x by 100.
So x is now 200.
Remember this x here, not this x here,
'cause we're capturing by value.
y is incremented by 100.
I'm sorry, that's 200 here.
No, that's correct, y was 200, now y is 300, that's correct.
And now I'm gonna output x and y, right?
Now let's call the Lambda.
This will execute, it gives us this,
and what we're going to display is right here,
200 and 300, these values.
When I come back from the function call,
I'm going to display x and y.
I'm not gonna display this x and this y,
not the ones in the Lambda.
So that's going to display the 100 and the 200.
Okay, so let's do a couple more
and then I'll switch over to another video
'cause this one's probably gonna get a little bit long.
In this case, we are doing default capture by reference.
That means we're capturing everything by reference.
Those are my three variables here, x, y, and z.
I'm capturing everything by reference,
so it's a single ampersand sign in the capture list.
So what does that mean?
That means that I've got an x, a y, and a z
that are references to the actuals, right?
So that's what we've got.
We're not making copies of these guys.
We've got references.
And we're gonna increment each by 100
and we're gonna display them, right?
So when I call this function,
what's gonna happen is this guy will become 200,
this guy will become 300 and z will become 400.
This function will display them, and we're in Test5,
so you can see right here, we're displaying 200, 300, 400.
Now, when we come back from the function,
we're gonna display these guys.
Well, those are the same ones we just displayed, right?
Because we actually changed the actuals
through these references.
Okay, so again, that should be pretty straightforward.
Hopefully everything is starting
to come together a little bit.
Once you get past the syntax,
I think it's pretty straightforward.
Let's do one more.
And in this case, it's Test6.
Let me scroll up here again a little bit.
Okay, so we're in Test6, we're right up here.
And in Test6, we've got those three variables again,
x, y, and z, and now what we're doing,
we're capturing by value and we're capturing by reference.
So that's the syntax here.
It says capture everything by value except y,
capture y by reference.
Okay, so again, capture everything by value,
but capture y by reference.
And it's mutable, so we can change
the values we're capturing.
So now we've got our x here, we're capturing x by value,
so I'm gonna copy the 100.
We're capturing y by reference.
So here's my y, it's a reference.
And my z, I'm capturing by value,
so my z is going to be 300 right here, which I've copied.
And then what I'm doing
is I'm just gonna add 100 to each one of them.
So I'm gonna call this Lambda.
What's gonna happen is I'm gonna increment x by 100.
That's this guy.
I'm going to increment y by 100.
That's my reference, so this guy now becomes 300.
And then I'm going to increment z by 100,
so this becomes 400.
Then I'm gonna display x, y, and z.
Well, that's 200, 300, 400.
And that's what we see right there.
When we come back from the function call,
I'm going to display x, y, and z.
Now we're gonna display these guys right up here,
x, y and z, so it's gonna display 100, 300, 300
and that's what you get right there.
Okay, so I'm gonna stop the video here
and then in the next video, I'll start from Test7,
and we'll start right from there.
I'll see you in the next video.