Hello again!
In this video, we are going to look at the bind() function. To see why this is useful,
let's start off by imagining we have some function which will compare its argument to a fixed value.
This function match() will return true if it is passed a string whose value is "cat".
And then, we could use this function as the predicate in an algorithm call.
We could have some container, some algorithm which takes iterators into this container, and then we
have match as the predicate.
So here is some code which does that. We have our match() function.
I am going to print out the argument, so we can see what is happening.
We have a vector of strings.
We have the count_if() algorithm.
This will iterate over all the elements in the vector.
It will call the match() function for each element and it will pass the element as the argument to match.
And it will return the number of elements for which the function returned
true.
So there we are.
We can see that the algorithm has called match(), with
each element as argument. And there are two elements for which the function would return true.
So this is all things that you've seen before.
Suppose we want to make this more general.
We do not want to have to compare to "cat" all the time.
The obvious thing to do is to add a second argument to the call to match, but unfortunately that is
not compatible with the syntax for algorithms.
There is no way to tell the algorithm directly that we want "cat" to be the second argument of the call to
match.
If we tried to do this, for example, then we get a compiler error. "Function"
does not take one arguments" (sic).
So this is where bind() comes in useful.
C++11 introduced bind().
It is in the header.
This takes a callable object as the first argument, and it will perform a partial function call.
It will bind the remaining arguments to this function and return a new callable object.
So if we call bind() with match() and "cat" as the arguments, this will bind "cat" to the match() function
and return a new object with the bound argument.
So if we call this returned object, it will call match() and pass "cat" as the argument.
So that corresponds to the case where we had match() with one argument, in the first example.
What we want is for match_cats() to take arguments.
So we can pass elements as well, and you can do that.
So if we pass an argument to match_cat(), it will be forwarded to match().
So for example, if we have arguments one and two, then match() will be called with these two arguments
and "cat".
Or alternatively, you can mingle them.
So you could have match with "arg1", followed by "cat", followed by "arg2".
To do this, we need some way of saying where the arguments are going to appear, in the call to match(),
and we have "placeholders" to do that.
So when we want "arg1", we put underscore one. For the argument which we want to have filled in by
"arg2", we put underscore two, and so on.
These placeholders have their own namespace, and we have to use that, to get them.
So, for example, we could call bind() with argument match placeholder one and "cat".
So this will now capture "cat" as the second argument to bind(), and this will return an object which
will call match() with the second argument as "cat".
And the first argument has to be provided by the caller of match_cat().
So, if we call match_cat() with argument "dog", for example, this will call match().
The first argument will be the argument from match_cat(), which is "dog".
And the second argument will be the bound argument, "cat".
So this will call match() with the arguments
"dog" and "cat".
So let's try that out.
The first part of the program is the same.
We have the same match() function. In our main() function, call bind().
We pass match() as the first argument, then the placeholder, then "cat". This will return an object
which binds "cat" as the second argument to match().
And will take one arguments, which will be the first argument to match().
We call match_cat() with argument "dog".
So this will call match(), with the argument "dog" in placeholder one,
and "cat". "Dog" does not equal "cat".
So we expect to see not matched. And that is what we get.
And we can also use this for our algorithm call.
So we have the same
match_cat() again. We have our vector of strings.
Then we have the count_if() call.
And then we pass match_cat().
So this count_if()
will iterate through all the elements of the vector. It will call match_cat with the element as
the first argument.
And that will call match(), with the element as the first argument.
And "cat" as the second argument.
So let's see what we get.
So the first argument is the element.
And the second argument is "cat".
So we have two occurrences of the word "cat". Incidentally,
you can put the arguments in a different order.
So if you have two arguments to match_cat(), you can put the second argument in a placeholder.
And then the first argument. So that result in the second argument being passed,
then "cat",
then the first argument.
If you can remember the video about partial evaluation and C++14 lambdas,
this may seem a bit familiar.
In fact, you could also implement this using a lambda with a local variable.
So we could have match_cat() as a lambda, with a local variable which is bound to "cat".
It takes the animal as argument, and then it calls match(), with "animal" and the bound variable as arguments.
So this time we defined match_cat() as this lambda expression.
So this has a lambda-local variable, species, which is equal to "cat".
It takes the animal as an argument, and then it calls match() with "animal" as the first argument and "species"
as the second.
And that gives exactly the same results.
So we now have two ways of doing this.
bind() and C++14
lambdas. Personally, I think the lambdas are better, because it is clearer to see, from looking at
the code, what it does.
And one last thing you may occasionally come across. In C++98, there are much more basic versions
of bind().
They are called bind1st() and bind2nd(). bind1st() would only bind the first argument, and nothing else.
bind2nd would bind the second argument, and nothing else.
So these are very inflexible, and not very convenient.
So that's why bind() was introduced in C++11. And then, in C++14, they realized they could use lambdas
instead.
So bind1st() and bind2nd() have been deprecated, and are now no longer part of C++.
bind() is still there, if you want to use it.
And we also have lambdas, which I think are the better alternative.
Okay, so that is it for this video.
I will see you next time.
But until then, keep coding!