Overloading the Function Call Operator in C++

In C++, overloading the function call operator, operator(), allows an instance of a class or struct to be invoked using standard function call syntax. These callable objects are known as functors or function objects. This article explains how to overload this operator, highlights the key advantages of functors over standard functions—particularly their ability to maintain internal state—and demonstrates practical examples, including their use with the Standard Template Library (STL).

What is a Functor?

A functor is any object that can be called as if it were a function. While a regular function executes a block of code, a functor combines executable code with object-oriented capabilities such as member variables, constructors, and encapsulation.

To create a functor, you define a class or struct and implement a member function named operator().

Basic Syntax and Implementation

The signature for overloading the function call operator can accept any number of arguments and return any type, just like a regular function.

#include <iostream>

class Greeter {
public:
    // Overloading operator() with one parameter
    void operator()(const std::string& name) const {
        std::cout << "Hello, " << name << "!\n";
    }
};

int main() {
    Greeter greet;
    
    // Invoking the object as a function
    greet("Alice");
    greet("Bob");

    return 0;
}

In this example, greet("Alice") is syntactically equivalent to calling the member function greet.operator()("Alice").

Maintaining State

The primary advantage of a functor over a regular function pointer is its ability to retain state across multiple invocations without relying on global variables.

#include <iostream>

class Multiplier {
private:
    int factor;

public:
    // Constructor initializes the state
    explicit Multiplier(int f) : factor(f) {}

    // Operator() uses the internal state
    int operator()(int value) const {
        return value * factor;
    }
};

int main() {
    Multiplier triple(3);
    Multiplier quintuple(5);

    std::cout << triple(10) << "\n";    // Outputs: 30
    std::cout << quintuple(10) << "\n"; // Outputs: 50

    return 0;
}

Here, triple and quintuple are two instances of the same class, but each maintains its own independent state.

Overloading with Multiple Signatures

Like other member functions in C++, operator() can be overloaded to handle different argument types, counts, or return types within the same class.

class Formatter {
public:
    void operator()(int n) const {
        std::cout << "Integer: " << n << "\n";
    }

    void operator()(double d) const {
        std::cout << "Float: " << d << "\n";
    }

    void operator()(const std::string& s, bool uppercase) const {
        std::cout << "String: " << s << (uppercase ? " [UPPER]" : "") << "\n";
    }
};

Using Functors with the Standard Template Library (STL)

Functors are widely used with STL algorithms such as std::sort, std::for_each, and std::transform. Because functors have distinct types, the compiler can often inline the function call, yielding performance benefits over raw function pointers.

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

struct CountDivisibleBy {
    int divisor;
    int count = 0;

    explicit CountDivisibleBy(int d) : divisor(d) {}

    void operator()(int n) {
        if (n % divisor == 0) {
            ++count;
        }
    }
};

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    // Pass functor to algorithm by value
    CountDivisibleBy result = std::for_each(numbers.begin(), numbers.end(), CountDivisibleBy(2));

    std::cout << "Count of numbers divisible by 2: " << result.count << "\n"; // Outputs: 5

    return 0;
}

In this case, std::for_each processes every element in the collection using the functor and returns the modified functor containing the accumulated state.