# Callable Objects
Callable objects are the collection of all C++ structures which can be used as a function. In practice, this are all things you can pass to the C++17 STL function invoke() or which can be used in the constructor of std::function, this includes: Function pointers, Classes with operator(), Classes with implicit conversions, References to functions, Pointers to member functions, Pointers to member data, lambdas. The callable objects are used in many STL algorithms as predicate.
# Function Pointers
Function pointers are the most basic way of passing functions around, which can also be used in C. (See the C documentation (opens new window) for more details).
For the purpose of callable objects, a function pointer can be defined as:
typedef returnType(*name)(arguments); // All
using name = returnType(*)(arguments); // <= C++11
using name = std::add_pointer<returnType(arguments)>::type; // <= C++11
using name = std::add_pointer_t<returnType(arguments)>; // <= C++14
If we would be using a function pointer for writing our own vector sort, it would look like:
using LessThanFunctionPtr = std::add_pointer_t<bool(int, int)>;
void sortVectorInt(std::vector<int>&v, LessThanFunctionPtr lessThan) {
if (v.size() < 2)
return;
if (v.size() == 2) {
if (!lessThan(v.front(), v.back())) // Invoke the function pointer
std::swap(v.front(), v.back());
return;
}
std::sort(v, lessThan);
}
bool lessThanInt(int lhs, int rhs) { return lhs < rhs; }
sortVectorInt(vectorOfInt, lessThanInt); // Passes the pointer to a free function
struct GreaterThanInt {
static bool cmp(int lhs, int rhs) { return lhs > rhs; }
};
sortVectorInt(vectorOfInt, &GreaterThanInt::cmp); // Passes the pointer to a static member function
Alternatively, we could have invoked the function pointer one of following ways:
(*lessThan)(v.front(), v.back()) // All
std::invoke(lessThan, v.front(), v.back()) // <= C++17
# Classes with operator() (Functors)
Every class which overloads the operator()
can be used as a function object. These classes can be written by hand (often referred to as functors) or automatically generated by the compiler by writing Lambdas (opens new window) from C++11 on.
struct Person {
std::string name;
unsigned int age;
};
// Functor which find a person by name
struct FindPersonByName {
FindPersonByName(const std::string &name) : _name(name) {}
// Overloaded method which will get called
bool operator()(const Person &person) const {
return person.name == _name;
}
private:
std::string _name;
};
std::vector<Person> v; // Assume this contains data
std::vector<Person>::iterator iFind =
std::find_if(v.begin(), v.end(), FindPersonByName("Foobar"));
// ...
As functors have their own identity, they cannot be put in a typedef and these have to be accepted via template argument. The definition of std::find_if
can look like:
template<typename Iterator, typename Predicate>
Iterator find_if(Iterator begin, Iterator end, Predicate &predicate) {
for (Iterator i = begin, i != end, ++i)
if (predicate(*i))
return i;
return end;
}
From C++17 on, the calling of the predicate can be done with invoke: std::invoke(predicate, *i)
.
# Remarks
A very useful talk by Stephan T. Lavavej (