In this video, we'll look at some of the motivating factors that led to the inclusion of lamb 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 functional
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 is 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 ID.
Now, these classes all have the same type of structure.
They might have attributes, constructors and so forth.
But the most important thing to have is an overloaded function call operator.
That's those open and closed persons 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 past 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 steel.
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 multi.
When we call the overloaded function call operator on this object, it will return whatever integer
we pass into it multiplied by ten.
So we can call the SQL algorithm transform and provide the iterator for the vector.
What this does is it starts at vector begin and ends at vector end and it starts writing back or transforming
the elements in vector 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 ten, 20, 30 and 40.
Now we don't have to create a name 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 read all these classes, each with the 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 display, and we can use this with
functional 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 see out.
So how can we use this class template?
It's actually pretty easy.
Let's create two functional objects from the display or template class.
The first will display integers and we'll call it D one and the second will display stud strings and
we'll call it DX two.
Notice how we provide the type and the template parameters.
We cannot use these function objects just as we would use functions.
We can call DX one and pass in 100 as an argument and we can call DX two 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 the DX one and DX two 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 one is a vector of the integers one, two, three, four and five and vec two
is a vector of strings.
Larry, moe and curly.
Now we use stood for each to display each of the elements provided by the iterator.
In the first example, we instantiate a display of iNts anonymously.
Notice the function call operator at the end.
That's important because we need to instantiate an object.
In the second example, we use D one, which we created in the previous slide and in the last example
we use DD to.
So now these functional 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.
Let me show you what that looks like.
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 display or template class or the function objects.
DX one and dx two.
We do have the same two vectors vec one and vec two.
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 forage.
So in this case, we're just displaying the data, but we can easily modify our lambda 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.
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 functional objects if we can mostly replace them with lambdas?
Well, it's because the compiler generates unnamed functional objects behind the scenes from the lambda
expressions.
And now you understand how they work.
Great.
So let's head over to the ID and we'll see some of this in live code.
Okay.
So I'm back in the ID, I'm in the Section 21 workspace and I'm in the Function Objects project.
And this project does a little bit of what we just did on the slides.
It's really straightforward, but I really want to 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 because 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 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 Factor, 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 can not 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 went straight forward.
And then I've got a template class here that expects my template parameter T and in this case it's called
disk player.
Again, I'm overloading that operator.
Write the function call operator.
It expects a T, whatever that is.
Could be in it.
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 going to be using instant strings, so I know it's going to 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 function.
It's one of these guys, right?
The only thing I can do with that object is call this 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 dot operator function call
and I'm passing in the four.
Well, hopefully that's clear.
It's really important that you understand this piece right here.
This is about a simple and example as I can come up with, because remember, lambdas are converted
into these functional 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 D one, the other one is called DD two.
I'm using this template class to do it, so I need to pass in my template parameter.
So this one dd one will display ints dd two will display std strings.
All we're doing here is outputting the data.
So it's going to be really simple.
We call DD one with 100, DD two 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 d one operator function call operator 100 DD two operator using the function call
operator again, we're passing in the string.
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 vec one as an integer vector.
One, two, three, four, five and VEC two is a vector of strings, Larry, Moe and Curly.
That's what we're going to work with.
And what we're going to do now is we're going to use the for each algorithm right here.
Remember, it's an algorithm, so it needs an iterator.
Here's the iterator.
I'm going to go from the beginning to the end of vector one.
That's one, two, three, four, five.
And at each iteration, the algorithm will pass in whatever the element is in that collection.
Right?
And that container, which is an integer, it's going to pass it into square.
So just like we did here, that's what's going to happen behind the scenes.
So it's going to pass in the one, the two, the three, the four and the five in that order.
And all we're doing with this func to right here with this function object is displaying.
So it's going to display the square of each of the numbers passed into it.
149 1625 you can see the output right there.
This does not change the vector, we're only displaying the vector.
So here we're going to display what we just did.
And actually this this one is that guy right there.
Right?
This this does no display right there.
So here for each again vec one begin to vec one end called dx one.
What's dx one will dx one is that display.
All it does is output the value passed into it.
So it's going to be passed in one, two, three, four, five and it's going to display them right here.
You can see it right there in this case, a 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 display or object right there.
I'm doing that in place.
I'm not naming it like I did DX one, DX two so I'm just calling display.
I'm using the template parameter stood string and notice right here, those are important because that's
what instantiating it.
That's not the function call operator, that's a constructor.
So that's creating that object.
So now I've got a display or object that can display strings.
I'll loop through the vector using the algorithm every time it's going to pass in an integer and it's
going to display Larry, Moe and Curly that's right there.
And then down here we're doing the same thing, except we're using the DX to function object, 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 going to use lambdas.
So all the stuff we just wrote up top, all those template classes and that square function, 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 going to 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 display.
So that's it.
What's going to 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 ten, we could just do that right there.
So you 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 going to happen is the for each will pass in each integer as x to the lambda expression.
In all this lambda expression it is doing is multiplying it by ten and displaying it.
That's it.
It's not changing anything.
It's just multiplying it by ten and displaying it.
So in this case, we're going to get ten, 20, 30, 40 and 50.
Here we're just displaying X.
You can see how we're displaying one, two, three, four and five, which tells you that that vector
was never changed.
And that was the whole point.
And then in the last example, we're using Vector now, which is a vector of strings.
Notice the parameter that's being passed into the lambda is a stood string now instead of an int.
We did the int up here, we did the 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 function, 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.
Last.
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 RN and all we're doing is assigning end to that attribute right there.
That's it.
We're just setting that instance variable and to 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 them change.
Make sure you put constant here.
So that's it.
So every time you call the function call operator on, multiply your 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.
It's a real, real simple multiplier.
All right, so now let's go down to my test for 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 we're using the function call operator.
Whatever I pass in, it's going to multiply it by 100.
If I wanted to multiply it by 30, I'd create another function object mult one and pass it to 30 to
it for example.
So let's see what the how this works.
I'm assigning one, two, three, four to my vector one and now I'm going to use to transform steward
transform does change the vector.
So it's a little bit different.
I think we used it once or twice in the steel section, but I'll go over it real quick.
So here's to transform.
It wants where to start, where to end and where to start making changes.
That's why you have these three iterations here.
So we have where to start.
I'm going to start at vector one begin.
I'm going to end at vector one's end.
Basically, I'm going to iterate through the entire vector one, which is again, one, two, three,
four and I'm going to start making changes in VEC one begin.
So it's going to start making changes here and here and here and here.
It's going to change all of them.
And what's it changing?
Well, it's using that object.
So what's going to happen is it's going to go through vector one, it's going to grab the one and it's
going to pass it into moult.
What's it going to do?
It's going to take that guy and multiply it by 100 and it's going to stick it back into the vector one's
position.
So it's going to make that 100.
It's actually going to transform the vector.
And so when we're done with this, we're just going to display it.
And you can see the display right here, 102 hundred, 301 hundred.
It actually changed that vector.
And then let's do it again.
What we're doing here is we're resetting that vector.
One, two, one, two, three, four.
And I'm just assigning one, two, three, four to it.
And what we're going to do now is we're going to do exactly the same thing, except we're not going
to use the function object molt.
We're just going to do it in a lambda.
Here's my lambda.
I'm using you to transform again starting at the beginning, going to the end of week one and start
transforming from the beginning of VEC one.
So every time I get an integer, all I'm going to do is return that integer times 100 and it's going
to be stored back into that vector.
It's going to transform the vector.
And now when I use my for each again and notice over here when we did the for each, we use that display
or function object.
This time we're using another lambda that just due to displaying all we're doing is just outputting
x, simple as that.
And when we do that you'll get exactly the same result.
102 hundred and 304 hundred.
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 that.
That's it.
That's that's the lambda.
And you can see the syntax.
There's my capsule 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 could do that.
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 expression in detail.
So I'll see you there.