(bright music)
(keys clicking)
Frank: In this video, we'll look
at some of the motivating factors that led
to the inclusion of lambda expressions in C++ 11.
Lambdas were a tremendous addition to C++.
Prior to C++ 11, we mainly used function objects
and function pointers when we needed custom behavior
with the standard template library,
and also with our own code.
Function objects were used mostly
with the standard template library,
and function pointers were used more often as callbacks.
Okay, so what's the problem?
Well, one of the problems is that we often ended up writing
many short functions that control algorithms.
And in the case of the STL,
these small functions were encapsulated
in small classes to produce function objects.
Now, that's okay and it works, but many times the classes
and the functions are defined far away
from when they're used, and this can lead to problems
with modifying, maintaining, and testing our code.
In addition, the compiler's not so good at optimizing
the functions when they're not defined in line.
Let's see an example.
In this example, we'll create a class
that will serve to provide function objects.
We'll see a couple of examples in these slides,
as well as when we go to the IDE.
Now, these classes all have the same type of structure.
They might have attributes, constructors,
and so forth, but the most important thing they have is
an overloaded function call operator().
That's those open and closed parens
that you see next to the operator().
You can see them in bold in the slide.
Whenever this operator() is used, the function associated
with it is called for an object of any Multiplier type.
So in this example,
we initialize Multiplier objects with an integer,
and we store that in the num attribute.
Then when we call the overloaded function operator
with a Multiplier object,
we multiply the passed in integer N by num,
and we return the result.
It's pretty simple, and these classes tend to be small,
but depending on our application,
we might end up with many variants of them.
So how do you use these classes?
Let's see an example with the STL.
Let's assume that we have that Multiplier class
that we just defined, and we have a vector called vec,
which is a vector of integers,
initialized to one, two, three and four.
So let's create a function object from the Multiplier class.
we'll call it mult, M-U-L-T.
When we call the overloaded function call operator()
on this object, it will return whatever integer we pass
into it multiplied by 10.
So we can call the STL algorithm transform,
and provide the iterators for the vector.
What this does is it starts at vec.begin()
and ends at vec.end(), and it starts riding back
or transforming the elements in vec from the beginning.
That's it, but now what do we do to transform that vector?
Well, that's where the function object comes in.
We iterate through the vector,
and we pass in each integer we see into mult.
When we're done, vec now has been transformed
to 10, 20, 30, and 40.
Now, we don't have to create a named Multiplier object.
We can simply create an unnamed Multiplier object,
as you can see here in bold.
We get the same behavior.
So what's wrong with all of this?
Well, nothing really.
It just becomes tedious to write all these classes,
each with a specific behavior
in the overloaded function call operator().
And sometimes it's not so obvious what they're doing
when you're looking at the existing code.
Here's another example using a template class
to create a generic Displayer,
and we can use this with function objects of any type
that overload the insertion operator().
Notice that we have no attributes in this class.
All we're doing is overloading the function call operator()
to display a T object to C out.
So how can we use this class template?
It's actually pretty easy.
Let's create two functional objects
from the Displayer template class.
The first will display integers, and we'll call it D1,
and the second will display std::strings,
and we'll call it D2.
Notice how we provide the type and the template parameters.
We can now use these function objects
just as we would use functions.
We can call D1 and pass in 100 as an argument,
and we can call D2 with Frank as the argument.
Behind the scenes,
the overloaded function call operator() is called,
and we display the appropriate result.
Now, this example really shows clearly
that D1 and D2 are objects,
but we're using them like functions,
and that's the reason they're called function objects.
But using them as in this example isn't all that useful,
so let's see a better way.
In this example, we have vec1 is a vector
of the integers one, two, three, four, and five,
and vec2 is a vector of strings, Larry, Moe, and Curly.
Now we use std::for_each to display
each of the elements provided by the iterators.
In the first example,
we instantiate a Displayer of int anonymously.
Notice the function call operator() at the end.
That's important, because we need to instantiate an object.
In the second example, we used D1,
which we created in the previous slide,
and in the last example we used D2.
So now these function objects display all the elements
in the vector, and we only had to write the class once,
since we wrote it as a generic template.
That's pretty cool and it works great,
but I think using lambda expressions is even cooler.
Lemme show you what that looks like.
Okay, so now let's do the same thing
with lambda expressions.
Note that all the code we need to do this is on this slide.
We don't need anything else.
We don't need that to Displayer template class
or the function objects D1 and D2.
We do have the same two vectors, vec1 and vec2.
Now in the for_each, we don't pass in a function object.
Instead, we use a lambda expression.
Don't worry about the syntax
of the lambda expression right now.
We'll go over that in detail soon, but do notice
that all the behavior we want is exactly where we want it,
right in line in the call to for_each.
So in this case, we're just displaying the data,
but we can easily modify our lambdas
to do whatever we want them to.
This makes the code more readable,
easier to test, easier to debug, and more maintainable.
Also, the compiler can more easily optimize this code.
Okay, so lambdas replace function objects, right?
Yes, and we write less code and more focused code, right?
Yes, so we never have to use function objects again, right?
Wrong.
Best practice with lambda expressions is to use them
when the amount of code is a statement or a few statements.
If you have more complex code,
you should consider using a function object.
One other benefit of a lambda expression is
that the compiler creates a closure object
from the lambda expression.
This means that we can gain access
to the environment in which the lambda exists.
This is super powerful,
and we'll see how it works pretty soon.
So why did I spend time talking about function objects
if we can mostly replace them with lambdas?
Well, it's because the compiler generates
unnamed function objects behind the scenes
from the lambda expressions,
and now you understand how they work.
Great, so let's head over to the IDE,
and we'll see some of this in LiveCode.
Okay, so I'm back in the IDE.
I'm in the section 21 Workspace,
and I'm in the FunctionObjects project.
And this project does a little bit
of what we just did on the slides.
It's really straightforward,
but I really wanna go over it to make sure
that you understand function objects.
Function objects, I've seen a lot
of beginning C++ programmers really not understand these,
and I think it's all about that operator(),
that function call operator() right here.
I don't know why.
It's just an operator().
Just 'cause it looks like a function,
don't let it freak you out.
It's just an operator(), just like the insertion
or the extraction or a plus or a minus, that's it.
It's just the way it's used is a little different,
but it should be very, very straightforward,
and hopefully, intuitive after this demo here.
Okay, so what we've got here is we've got three classes.
These two are structures.
This one down here is a class, Multiplier,
which I'll get to in a second.
I'll just keep it closed for now.
So what I'm doing here is I'm creating
this structure right here.
It's called Square_Functor, and all it does is
that it overloads the function call operator() right there.
That's it, that's all it's doing.
By doing that, you cannot create function objects,
and I'll show you exactly how that works in a second.
So I'm overloading that operator(),
and it expects an integer, and when I get that integer,
I'm simply displaying the square, that's it.
I'm not changing anything, I'm only displaying output only.
Okay, so that one's straightforward.
And then I've got a template class here
that expects my template parameter T,
and in this case, it's called Displayer.
Again I'm overloading that operator(), right?
The function call operator().
It expects a T, whatever that is,
it could be an int, it could be a string,
it could be just about anything,
and all I'm doing again is outputting the data.
So in this case,
we have to make sure that we are overloading
that insertion operator() with whatever type we're using.
I'm only gonna be using ints and strings,
so I know it's gonna work just fine with them.
Okay, so let's go over main.
Here is my test one.
You can see the output right up here on the right.
And what I'm doing is I'm creating an object,
and that object's name is square.
It's type is Square_Functor.
It's one of these guys, right?
The only thing I can do
with that object is call its function call operator().
That's it, there's nothing else to do.
There's no attributes, there's no other methods.
So this is about as simple a function object
as you can get.
So now once I have square,
I can say square and pass in a four.
Now, this looks exactly like a function call, doesn't it?
And that's the whole point.
What's really happening behind the scenes is,
remember, this square is not a function, it's an object.
So what's really happening behind the scenes is
that we're calling squares.operator() function call,
and I'm passing in the four.
Okay, hopefully that's clear.
It's really important
that you understand this piece right here.
This is about as simple an example as I can come up with,
because remember, lambdas are converted
into these function objects behind the scenes.
So in this case, we display a 16,
and you can see the 16 displaying right here.
In this case, I'm creating two function objects.
One is called D1, the other one is called D2.
I'm using this template class to do it,
so I need to pass in my template parameter.
So this one, D1 will display ints.
D2 will display std::strings.
All we're really doing here is outputting the data,
so it's gonna be really simple.
We call D1 with 100, D2 with Frank,
and we get right out here, 100 and Frank.
Again these guys look just like function calls, right?
But they're not.
They're function objects.
What's happening is D1.operator(),
function call operator() 100,
D2.operator() using the function call operator().
Again we're passing in the string, okay?
So again that's about as simple an example
as I can come up with
that really drives home the point here.
They look like function calls, they're not,
they're function objects.
All right, so let's move on to test two.
And what we'll do here is we'll use these function objects
with our standard template algorithms.
So here is my test two right here,
and you can see the output beginning up here on the right.
All I'm doing is creating vec1 as an integer vector,
one, two, three, four, five,
and vec2 is a vector of std::strings,
Larry, Moe, and Curly.
That's what we're gonna work with.
And what we're gonna do now is we're gonna use
the for_each algorithm right here.
Remember it's an algorithm, so it needs an iterator.
Here's the iterator.
I'm gonna go from the beginning to the end of vector one,
that's 1, 2, 3, 4, 5, and at each iteration,
the algorithm will pass in whatever the element is
in that collection right in that container,
which is an integer, it's gonna pass it into square.
So just like we did here,
that's what's gonna happen behind the scenes.
So it's gonna pass in the one, the two, the three, the four,
and the five, in that order.
And all we're doing with this _Functor right here,
with this function object is displaying.
So it's going to display the square of each of the numbers
passed into it, one, four, nine, 16, 25.
You can see the output right there.
This does not change the vector,
we're only displaying the vector.
Okay, so here we're gonna display what we just did,
and actually, this one is that guy right there.
There's no display right there.
So here, for_each again vec1 begin
to vec1 data and call D1, what's D1?
Well, D1 is that Displayer.
All it does is output the value passed into it,
so it's going to be passed in 1, 2, 3, 4, 5,
and it's gonna display them right here.
You can see it right there.
In this case, little bit different,
what I'm doing is I'm using the for_each, and here,
I'm using vector two, which is Larry, Moe, and Curly.
I'm going from the beginning to the end,
and I'm creating a Displayer object right there.
I'm doing that in place.
I'm not naming it like I did D1 D2,
so I'm just calling Displayer
I'm using the template parameter std::string,
and notice right here,
those are important because that's what instantiates it.
That's not the function call operator(),
that's a constructor, so that's creating that object.
So now I've got a Displayer object that can display strings.
I'll loop through the vector using the algorithm.
Every time it's gonna pass in an integer,
and it's going to display Larry, Moe, and Curly.
Okay, that's right there.
And then down here, we're doing the same thing,
except we're using the D2 function objects,
which is the one we created earlier,
and it does exactly the same thing.
It just prints out Larry, Moe, and Curly.
So now let's go on to test three.
And we're right here on line 62.
So what we're doing again now,
this time we're gonna use lambdas.
So all the stuff we just wrote up top,
all those template classes and that Square_Functor,
and all that stuff, we're not using any of that now.
We're doing it all using lambdas,
so this code right here is all self-contained.
It needs nothing else, which is kind of cool.
So right here, we're using for_each,
and we're going from vector one beginning, vector one end.
And here's my lambda expression,
it starts here with those square brackets like that.
We'll get used to that syntax in a second,
and I'll talk about the syntax in detail in the next video.
I just wanted to show you what it looked like.
And lambda ends right there.
So now what's gonna happen is
the for_each algorithm will send in an integer right in here
to the lambda, and it's X.
And all we're doing is just displaying X squared.
We're not changing X, we're not changing the vector,
we're simply displaying the elements.
The same thing we did with the Displayer.
Okay, so that's it.
What's gonna happen is it's going to print
out one, four, nine, 16, 25,
and you can see that right here in test three.
Remember, we haven't changed the vector.
Now, if we wanted to take each one of those integers
that's being passed into the lambda
and multiply it by 10, we could just do that right there.
So hopefully, you can see how clear this is.
It may not be clear the first time you see it.
I remember the first time I saw lambdas,
and I've used them in other languages as well.
The syntax is always a little tricky at the beginning,
but after a little bit, it becomes second nature.
So what's gonna happen is the for_each will pass
in each integer as X to the lambda expression,
and all this lambda expression is doing is multiplying it
by 10 and displaying it, that's it.
It's not changing anything,
it's just multiplying it by 10 and displaying it.
So in this case, we're gonna get 10, 20, 30, 40 and 50.
Here, we're just displaying X.
And you can see how we're displaying 1, 2, 3, 4, and five,
which tells you that that vector was never changed,
and that was the whole point.
And then the last example, we're using vec2 now,
which is a vector of std::strings.
Notice the parameter that's being passed
into the lambda is a std::string now instead of an int.
We did the int up here, we did the std::string here.
This is really nice
that you can code what you want right in here.
This is what I want to do.
I'm coding it right in there, that's all I need to do.
I don't need to look through my source files
to figure out where my my function object class is,
or anything like that.
It's right in here, right in place, which is kind of cool.
In this case, I'm just displaying Larry, Moe, and Curly,
and you can see them right there.
All right, so now let's do the last one,
and in order to do the last one,
we're using this Multiplier class,
so let me scroll up and I'll show you that.
So this one's a little bit more complex, but not much.
This is the same one we did on the slides.
Here, I've got a class,
and it's got a private attribute right here called num.
It's got a constructor right here,
you can see the user passes in an N,
and all we're doing is assigning N
to that attribute right there, that's it.
We're just setting that instance variable.
And notice right here,
we're overloading the function call operator().
That's what makes it a function object
when we create function objects from this class, okay?
Best practice is when you've got variables
or attributes in here and you don't want 'em changed,
make sure you put constant in here.
So that's it, so every time you call
the function call operator() on Multiplier objects,
it's going to take in whatever you pass in, which is N,
and multiply it by whatever we set num to,
that's all it's doing.
So it's a real, real simple Multiplier.
All right, so now let's go down to my test four right here.
You can see right on line 75 is where it begins,
and the output is right over here.
I'm creating a function object right here called mult,
and I'm passing in 100 to it.
So remember, I could just simply pass anything into that now
using the function call operator(),
and whatever I pass in, it's gonna multiply it by 100.
If I wanted to multiply it by 30,
I'd create another function object, mult1,
and pass it a 30 to it, for example.
Okay, so let's see what, how this works.
I'm assigning 1, 2, 3, 4 to my vector one,
and now I'm gonna use std::transform.
Std::transform does change the vectors.
It's a little bit different.
I think we used it once or twice in the STL section,
but I'll go over it real quick.
So here's std:transform.
It wants where to start, where to end,
and where to start making changes.
That's why you have these three iterators here.
So we have where to start.
I'm gonna start at vector one begin.
I'm going to end at vector one's end.
Basically, I'm gonna iterate through the entire vector one,
which is again, 1, 2, 3, 4,
and I'm gonna start making changes in vec1 begin.
So it's gonna start making changes here
and here and here and here, it's gonna change all of them.
And what's it changing?
Well, it's using that object.
So what's gonna happen is it's gonna go through vec1,
it's gonna grab the one, and it's gonna pass it into mult.
What's mult gonna do?
It's gonna take that guy and multiply it by 100,
and it's gonna stick it back into vector one's position,
so it's gonna make that 100.
It's actually going to transform the vector, right?
So when we're done with this,
we're just going to display it,
and you could see the display right here
100, 200, 300, 400, it actually changed that vector.
And then let's do it again.
What we're doing here is we're resetting
that vector one to 1, 2, 3, 4.
I'm just assigning 1, 2, 3, 4 to it.
And what we're gonna do now is
we're gonna do exactly the same thing,
except we're not gonna use the function object mult.
We're just gonna do it in a lambda.
Here's my lambda.
I'm using std::transform again, starting at the beginning,
going to the end of vec1,
and start transforming from the beginning of vec1.
So every time I get an integer,
all I'm going to do is return that integer times 100,
and it's gonna be stored back into that vector.
It's gonna transform the vector.
And now when I use my for_each again,
and notice over here when we did the for_each,
we used that Displayer function object.
This time, we're using another lambda
just to do the displaying.
All we're doing is just outputting X, simple as that.
And when we do that, you'll get exactly the same result,
100, 200, 300, 400.
Okay, so this gives you a little bit
of a taste for function objects and lambdas.
In the next video,
what we'll do is we'll take this syntax right here,
this is actually all you need to remember,
square brackets, ta-da.
That's it, that's the lambda, okay?
And you can see the syntax.
There's my capture list.
This is where my parameters come in.
There's my code.
Now, there's a couple of other bits
that you can throw in here.
You can do that, and you can throw some keywords in there,
and we'll talk about all of that in the next video
when we go over the structure
of the lambda expression in detail,
so I'll see you there.