Hello again!
In this video, we are going to look at callable objects.
A callable object is anything which supports the function call operator.
C++ now has quite a few of these.
There's the original pointer to function from C, which still works, with non-member functions. Pointers
to member functions are quite different.
We will be looking at those in the next video. Functors: classes which define the function called operator.
Lambda expressions, which are functors written by the compiler. And the result of calling bind().
So these are all callable objects.
We can write the name of the object, followed by a pair of brackets which contain arguments, and this will
result in a function being called.
These callable objects all have different types. If we have a pointer to a function which takes string
and returns int, this will be a different type from a lambda expression which takes string and returns
int.
Sometimes it's useful to be able to work with different callable objects which have the same signature,
the same arguments and return type.
And now there is a way of doing that.
C++11 has a standard function class in the header.
This has a private member which stores a callable object.
It is a template class and the parameter of the template is the signature of the object.
This looks a bit strange at first.
We have the return type, then open bracket, then the arguments, then close bracket.
So that is similar to the function prototype, but without the name of the function. And the member can
store a callable object which has this signature.
The function class is implemented using inheritance and virtual functions.
It performs what's known as "type erasure".
So if you have a function object, you cannot tell what type of callable object is in there.
You don't know whether it is a lambda, or a functor, or anything else.
So why would this be useful?
One thing you can do is, you can have a function call, and you can say that one of the arguments to this
function is a function object.
And then in that argument you can pass any kind of callable object which has the right signature.
You can also create a container of function objects, and you can populate that with any kind of
callable object with the right signature.
There are some limitations.
The function prototype must match the template parameter, so there is no support for overloading.
And also there is runtime overhead. Because it is implemented using inheritance and virtual functions,
then invoking the callable object involves an indirection.
So that is similar to virtual function overhead.
Another limitation is that the function class assumes that all callable objects have the same size.
If you have a functor, that may not be true. A function is a class. It can have data members, and those
data members can make the object bigger than the space that is available.
In that case, the function object will allocate memory on the heap, to make sure it has enough room.
So there is no danger of an overflow.
But that does add to the runtime and the memory usage.
So because of these limitations, we should really only use the function class when we need a polymorphic
function object.
If you just want somewhere convenient for storing a callable object, use a variable and declare its
type as auto.
So for example, a function pointer, or a lambda expression. You just have a variable of type "auto".
So this will retain the type of the callable object.
The compiler will know what the type is. And you can call it directly, just like you would normally.
There is no overhead from indirection.
So, here is some code for trying this out.
We are basically going to repeat the exercise from the last video, where we had a vector of strings, and
we were counting the number of cats. In that one, we used the count_if() function from the header.
This time, we are going to implement our own version of count_if(), using a function object as the argument.
So here it is. We have our function, count_strings().
This will take a vector of strings.
And then we have a function object.
This will iterate through the elements of the vector, and it will call the function for each element.
And it will count the number of times that the function returns
true.
So that is basically the functionality of count_if().
We have various functions we are going to pass to it.
We have a non-member function.
We have functor, which defines a function call operator.
We have this function, which we are going to pass to bind().
And then we also have a lambda expression,
later one in the main().
So here is our vector of strings again.
Then we call count_strings(). First of all, with a function pointer.
So that is a pointer to the non-member function.
Then we call it with the functor.
We need to pass an object of the functor.
So we call the constructor.
Then we have our lambda expression.
And then we call bind().
So this returns a callable object. And then we pass the callable object to our count_strings() function.
So these should all return the same value.
Okay.
And there we are.
So that is it for this video.
I will see you next time.
But until then, keep coding!