Overloading function-call operator to create Functors (function objects)

Question | Jan 19, 2017 | rparekh 

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.

enter image description here

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):

2015 nextptr