Hello again! In this video, we are going to have an introduction to lambda expressions. When we call
an algorithm function, like find_if(), we have been using functors, or sometimes function pointers.
So we pass an object of a functor class or a function pointer as the callable object, and that will
customize the way the find_if() call actually processes the elements.
In this case, we have a class, is_odd.
It has an overloaded function call operator, which takes an int. And it will return
true if that int argument is odd.
So if you imagine you were writing this code. You write your main() functions, you declare your vector.
Then you start writing your find_if() call, begin(), end() - ah! I need a callable object.
So you have to scroll up before the start of the current function.
Find a bit of empty space.
Think of a unique name for your class or function, and then write the code. And then you come back here.
And then, "what was I doing?"
Oh yes, find_if().
So it is all an unnecessary interruption.
And also, all this code does take up space.
What if we could actually watch the code inside the algorithm call? So we could just put the
return n mod two equals one in here?
Would that not be better? In modern C++,
we have lambda expressions, which allow us to do this.
These are, if you like, anonymous local functions.
Other languages have similar things, sometimes called closures. And no doubt, debates about whose implementation
is the best or the most accurate!
When the compiler encounters one of these lambda expressions, it will generate some code, which defines
a functor class.
So this class will have a name, which is chosen by the compiler. And it is guaranteed to be unique, and
not to conflict with anything else in scope.
This class will have a function call operator.
The body of this will be the same as the code in the lambda expression. And the return type of this
function call operator will be the same as the lambda expression.
And usually, the compiler will be able to work out what the return type is.
And then the compiler will add code, which will create an object of this class, at the point where the
lambda expression appears in the code.
Lambda expressions are anonymous, and they're defined in lines. So we just write the code where the lambda
expression is.
We start off by putting a pair of square brackets, where the function name would go.
That indicates that we are starting a lambda expression.
Then we have a pair of round brackets which contain the function arguments. Just like they do for a normal
function.
So there we are, we have square brackets and the arguments. And then we have a pair of curly braces which contain
the actual code that we want to be executed.
So that is just like writing an inline function.
If we find this as a single statement, which returns a value, the compiler will be able to deduce
the return type, in all versions of C++, which support lambda expressions.
So in this case, the compiler can see that this will return a bool. And the return type from the function
call operator in the class that it generates will be bool. So the compiler will generate the
code for this functor class.
And it will also add some code which will create an object of this class.
And the functor class that the compiler generates will be exactly equivalent to the class that we
had in our example.
So the algorithm call would look like this.
We have the iterator range again, and then, for the callable object, we write a lambda expression.
So that is the pair of square brackets, the argument and then the inline code.
So here is the code again with a lambda expression.
And you will notice it is much shorter.
So we have the vector of ints again.
Then we have the find_if() call, and there is the lambda expression.
So let's see if this works.
Yep, first odd element is 3.
So this is much easier to write.
You can start off declaring the vector. Then you write your find_if(), begin(), end(). "What do I want this
to do? Oh yes, if n mod two is equal to one..."
And then you can carry on, without having to interrupt yourself.
And if you are reading the code as well, this is much easier to follow.
The code is actually where it is used, as opposed to somewhere else.
As C++ has developed and we have more experience with lambda expressions, compilers are getting better
at deducing return types. In C++ 11, the first version with lambda expressions,
the compiler can only deduce the return type if the expression is a single statement, which returns
a value. For anything else,
even if you have multiple statements which return a value, the compiler will put void.
If you want a return type, then you have to put it yourself, with this arrow followed by the return
type.
In C++14, the compiler can deduce the return type, provided it is the same in all branches.
So if we put an if statement in here, then the if clause and the else clause would both have to return
a bool. In C++17, that is no longer required.
So you can have one branch which returns int, and another one which returns string, and so on.
And the compiler will deduce the return type?
Okay, so that is it for this video.
I will see you next time.
Until then, keep coding!