C++ Functors


Introduction

In C++, a functor is an object that behaves like a function. In simpler terms, a functor is any object that overloads the function call operator (operator()), allowing it to be called like a regular function. Functors are commonly used for passing behaviors to algorithms, storing function-like objects, and improving code modularity and reusability.

Functors are often used in the Standard Template Library (STL), particularly with algorithms like std::sort(), where you might need custom comparison logic.


Syntax for Functor

To create a functor, you define a class or struct and overload the function call operator (operator()).

class Functor {
public:
    // Overloading the function call operator
    void operator()() {
        std::cout << "Functor called!" << std::endl;
    }
};

In the above code, operator() allows an instance of Functor to be called like a function.


Example 1: Basic Functor Example

#include <iostream>

// Functor class
class Printer {
public:
    void operator()(const std::string& msg) const {
        std::cout << "Message: " << msg << std::endl;
    }
};

int main() {
    Printer printer;  // Create a functor object
    printer("Hello, Functor!");  // Call it like a function
    
    return 0;
}

Output:

Message: Hello, Functor!

In this example, the Printer class has an overloaded operator() that takes a string and prints it. You can call printer("Hello, Functor!") just like a function.


Example 2: Functor with Parameters

Functors can also take parameters. This allows them to behave like functions that accept arguments.

#include <iostream>

// Functor class
class Adder {
public:
    int operator()(int a, int b) {
        return a + b;
    }
};

int main() {
    Adder add;  // Create a functor object
    int result = add(5, 10);  // Call it like a function
    std::cout << "Sum: " << result << std::endl;
    
    return 0;
}

Output:

Sum: 15

In this example, the Adder functor takes two integers and returns their sum when called.


Functors in STL Algorithms

Functors are commonly used in STL algorithms when you need to pass a custom function or comparison logic.

Example 3: Functor with std::sort()

One common use of functors is to provide custom sorting logic to STL algorithms like std::sort().

#include <iostream>
#include <vector>
#include <algorithm>

// Functor class to compare two integers
class Compare {
public:
    bool operator()(int a, int b) {
        return a > b;  // Sort in descending order
    }
};

int main() {
    std::vector<int> vec = {10, 30, 20, 50, 40};
    
    // Use the functor to provide custom comparison to std::sort
    std::sort(vec.begin(), vec.end(), Compare());
    
    // Print the sorted vector
    for (int num : vec) {
        std::cout << num << " ";
    }

    return 0;
}

Output:

50 40 30 20 10

In this case, the Compare functor defines a custom comparison operator to sort the vector in descending order.


Benefits of Functors

  1. Stateful Functions: Functors can maintain internal state between calls. This allows for more complex behaviors, like keeping track of the number of times a functor has been called.

    class Counter {
    private:
        int count = 0;
    public:
        void operator()() {
            count++;
            std::cout << "Called " << count << " times." << std::endl;
        }
    };
    
  2. Flexibility: Functors can be more flexible than regular function pointers. They allow for custom behavior and can store additional data or state, unlike plain functions.

  3. Compatibility with STL: Many STL algorithms (like std::sort(), std::for_each(), etc.) work seamlessly with functors, allowing you to customize their behavior without needing to define separate functions.


Functors vs. Lambda Expressions

In C++11 and later, you can use lambda expressions, which are function-like objects defined inline. Lambdas are similar to functors, but they're often shorter and easier to use in specific contexts.

Example: Lambda Expression vs. Functor

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> vec = {10, 30, 20, 50, 40};

    // Using a lambda expression to sort the vector in descending order
    std::sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; });
    
    // Print the sorted vector
    for (int num : vec) {
        std::cout << num << " ";
    }

    return 0;
}

Output:

50 40 30 20 10

In this example, the lambda expression [](int a, int b) { return a > b; } performs the same task as the Compare functor in the previous example.

While both functors and lambdas can be used in similar scenarios, lambdas are generally more concise and easier to use, but functors are still preferred when you need to store state or reuse logic.


Functors with State

A key advantage of functors over lambda expressions is that they can maintain internal state. This is useful when you want to preserve information between calls.

Example: Functor with State

#include <iostream>

// Functor with state
class Multiplier {
private:
    int factor;
public:
    Multiplier(int f) : factor(f) {}

    int operator()(int num) {
        return num * factor;
    }
};

int main() {
    Multiplier multiplyBy2(2);  // Create a functor that multiplies by 2

    std::cout << "2 * 5 = " << multiplyBy2(5) << std::endl;  // Call the functor

    return 0;
}

Output:

2 * 5 = 10

In this example, the Multiplier functor maintains a factor that can be customized when creating the functor object. This state can be used across multiple calls to the functor.