Why override operator()?

71,252

Solution 1

One of the primary goal when overloading operator() is to create a functor. A functor acts just like a function, but it has the advantages that it is stateful, meaning it can keep data reflecting its state between calls.

Here is a simple functor example :

struct Accumulator
{
    int counter = 0;
    int operator()(int i) { return counter += i; }
}
...
Accumulator acc;
cout << acc(10) << endl; //prints "10"
cout << acc(20) << endl; //prints "30"

Functors are heavily used with generic programming. Many STL algorithms are written in a very general way, so that you can plug-in your own function/functor into the algorithm. For example, the algorithm std::for_each allows you to apply an operation on each element of a range. It could be implemented something like that :

template <typename InputIterator, typename Functor>
void for_each(InputIterator first, InputIterator last, Functor f)
{
    while (first != last) f(*first++);
}

You see that this algorithm is very generic since it is parametrized by a function. By using the operator(), this function lets you use either a functor or a function pointer. Here's an example showing both possibilities :

void print(int i) { std::cout << i << std::endl; }
...    
std::vector<int> vec;
// Fill vec

// Using a functor
Accumulator acc;
std::for_each(vec.begin(), vec.end(), acc);
// acc.counter contains the sum of all elements of the vector

// Using a function pointer
std::for_each(vec.begin(), vec.end(), print); // prints all elements

Concerning your question about operator() overloading, well yes it is possible. You can perfectly write a functor that has several parentheses operator, as long as you respect the basic rules of method overloading (e.g. overloading only on the return type is not possible).

Solution 2

It allows a class to act like a function. I have used it in a logging class where the call should be a function but i wanted the extra benefit of the class.

so something like this:

logger.log("Log this message");

turns into this:

logger("Log this message");

Solution 3

Many have answered that it makes a functor, without telling one big reason why a functor is better than a plain old function.

The answer is that a functor can have state. Consider a summing function - it needs to keep a running total.

class Sum
{
public:
    Sum() : m_total(0)
    {
    }
    void operator()(int value)
    {
        m_total += value;
    }
    int m_total;
};

Solution 4

A functor is not a function, so you cannot overload it.
Your co-worker is correct though that the overloading of operator() is used to create "functors" - objects that can be called like functions. In combination with templates expecting "function-like" arguments this can be quite powerful because the distinction between an object and a function becomes blurred.

As other posters have said: functors have an advantage over plain functions in that they can have state. This state can be used over a single iteration (for example to calculate the sum of all elements in a container) or over multiple iterations (for example to find all elements in multiple containers satisfying particular criteria).

Solution 5

You may also look over the C++ faq's Matrix example. There are good uses for doing it but it of course depends on what you are trying to accomplish.

Share:
71,252
JeffV
Author by

JeffV

Electronics Technologist, currently working as a Software Engineer on VR Simulation Systems. Primarily work in Modern C++, C (embedded), Java, C#. Involved in full engineering life cycle including requirements analysis, system decomposition, high level and low level design, implementation and system verification. Designed and implemented sonar processing systems for beam forming line arrays using distributed services and message passing with ZeroMQ. Designed and implemented VR training simulators with hardware stimulation. Designed and implemented wireless blasting receivers for through the earth communications. Extreme low power and reliability design goals: 6mos on a single C-cell battery. Spend my spare time with my wife and daughter, and enjoy mountain biking, boating, scuba, photography and flying. I like to ask the good questions so all can benefit from the answers. Hard to beat the fastest guns in the west around here. ;-)

Updated on July 05, 2022

Comments

  • JeffV
    JeffV almost 2 years

    In the Boost Signals library, they are overloading the () operator.

    Is this a convention in C++? For callbacks, etc.?

    I have seen this in code of a co-worker (who happens to be a big Boost fan). Of all the Boost goodness out there, this has only led to confusion for me.

    Any insight as to the reason for this overload?

  • altruic
    altruic over 15 years
    Your last line is not an operator overload at all. It needs to be: ".operator()(int n, float f)" which looks very confusing the first time you see it. You can overload this "function call operator" like like other functions, but you cannot overload it with the non-operator overload you specified.
  • Ehab Developer
    Ehab Developer over 15 years
    Your second line is wrong, it's actually "my_functor.operator()();". my_functor.operator() is the method reference, while the second set of () denotes the invocation.
  • JeffV
    JeffV over 15 years
    That doesn't explain why there is a need to hide that fact that it is an object and masquerade it to be a function.
  • JeffV
    JeffV over 15 years
    I think a big part of this answer is the syntax of the STL for_each. By using the operator() as the operation part of the functor it will work well with the STL.
  • JeffV
    JeffV over 15 years
    It seems that if the STL was implemented as do(){ ... } instead of operator()(){ ... } do would be used instead.
  • josesuero
    josesuero over 15 years
    Another (usually minor) advantage of functors over functions is that they can be trivially inlined. There's no pointer indirection involved, just calling a (nonvirtual) member function on a class, so the compiler can determine which function is called, and inline that.
  • josesuero
    josesuero over 15 years
    Jeff V: Convenience. It means the same syntax can be used to make the call, whether we're calling a functor or a function pointer. If you look at std::for_each, for example, it works with either functors or function pointers, because in both cases, the syntax for the call is the same.
  • josesuero
    josesuero over 15 years
    deleted my comment on why operator() is chosen specifically, since you edited that into your post :)
  • Geekoder
    Geekoder over 15 years
    The advantage of using the operator() is that your template parameter can be equally a function pointer or a functor.
  • Steve Jessop
    Steve Jessop over 14 years
    'prints "10 30"': Actually, it might print "20 30". The order of evaluation of subexpressions is not specified, except that the operands of each << have to be evaluated before that << is (and hence, because of the way << associates, the order in which the parts are printed is defined). There's nothing to say that the operand of the last << must be evaluated after the operand of the first <<, just that the last << must itself be evaluated after the first. The compiler is allowed to get that acc(20) done early.
  • Mark Ransom
    Mark Ransom over 9 years
    @jalf isn't operator() a function as well? So how could it have an advantage for inlining over any other function? The only way it could make any difference is simply a matter of convention; member functions are often written inline while free-standing functions are not. It's easy to change if you know you will be using the free-standing function in a context where inlining would be valuable.
  • josesuero
    josesuero over 9 years
    @MarkRansom yes, it is. However, think about what the compiler sees in for_each in the example above. If I pass it a free function for the third parameter, then it would instantiate for_each for the type int(*)(int). At compile-time, that doesn't tell us anything about what is called. It tells us that at runtime we're going to get a pointer, and that pointer will tell us which function to call. The compiler doesn't know which function will be called, so it can't inline the call
  • josesuero
    josesuero over 9 years
    @MarkRansom However, if you instead pass a functor, such as the Accumulator defined above, then for_each is instantiated for Accumulator, and the function that is called in its body is Accumulator::operator()(int). So the compiler knows which function is going to be called, regardless of the actual value being passed in at runtime. That allows it to trivially inline the call