A function object or a Functor is an instance of a class that overloads the function-call operator '()'. Functors are generally used as callback functions. They have advantages over standard function pointers because, being objects, they can have properties and maintain state.
Let's look at a simple use case of Functor to filter the objects of a class Employee from a collection to another collection:
struct Employee {
std::string name_;
int salary_;
std::string department_;
//.. more members
};
And, a collection of Employees:
/* Collection of Employees.
We use C++11 list-initialization here.
Fill the collection with multiple push_back calls
if your compiler doesn't support list-initialization.
*/
std::vector<Employee> employees = {
{"Sam", 50000, "sales"},
{"Mac", 70000, "marketing"},
{"Dot", 90000, "research"},
{"Ted", 60000, "sales"},
};
For the sake of simplicity, we are storing Employee objects directly within the STL collection. Note that, moving the elements within a collection (e.g. sorting) invokes copy/move operations (copy/move constructor or assignment operator). If the stored objects are big or frequent copy/move operations are expected, it could be better to store the pointers to objects in the collection.
We use STL algorithm std::copy_if to filter the sales department Employees from collection employees to another collection sales_employees. The last argument to std::copy_if is a predicate that can be a function pointer, a functor, or (since C++11) a lambda expression.
std::vector<Employee> sales_employees;
/* std::copy_if(employees.begin(),
employees.end(),
std::back_inserter(sales_employees),
predicate);
*/
A predicate is a function that would be called by std::copy_if for each Employee object in employees to decide if the object can be copied to sales_employees or not. A simple predicate can be a pointer to a global function, and can be used as follows:
bool is_sales_employee(const Employee& e) {
return e.department_ == "sales";
}
std::copy_if(employees.begin(),
employees.end(),
std::back_inserter(sales_employees),
is_sales_employee);
As you can see that the global function - is_sales_employee - is not generic because it is for only sales department. We can do a better job by creating a functor class - IsDeptEmployee - and using its instances as predicates to filter employees of any department:
// A functor class for any department
struct IsDeptEmployee {
explicit IsDeptEmployee(const char* dept)
:department_(dept){ }
/* Overloaded operator () to create a functor */
bool operator () (const Employee& e) {
return e.department_ == department_;
}
std::string department_;
};
Now, we can use the instances of IsDeptEmployee like regular functions, e.g.:
IsDeptEmployee isResearchEmployee("research");
if( isResearchEmployee(employees[2]) ) // calling 'operator ()'
std::cout << employees[2].name_;
And, this is how we can use IsDeptEmployee's instance to filter sales employees:
std::copy_if(employees.begin(),
employees.end(),
std::back_inserter(sales_employees),
IsDeptEmployee("sales"));
Suppose we want to sort the employees collection by salaries of Employees in descending order. We are going to use another STL algorithm std::sort whose last argument - just like predicate in std::copy_if - is a comparator. The comparator could be a functor that takes two arguments for comparison and returns true if the first argument is less than the second:
struct SalaryComparator {
// overloaded operator ()
};
std::sort(employees.begin(),
employees.end(),
SalaryComparator());
Select the correct definition of overloaded operator () in SalaryComparator for sorting in descending order (check Explanations for details):