82

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?

John Carter
  • 53,924
  • 26
  • 111
  • 144
JeffV
  • 52,985
  • 32
  • 103
  • 124

12 Answers12

186

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

jacknad
  • 13,483
  • 40
  • 124
  • 194
Luc Touraille
  • 79,925
  • 15
  • 92
  • 137
  • 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 Nov 25 '08 at 14:52
  • It seems that if the STL was implemented as do(){ ... } instead of operator()(){ ... } do would be used instead. – JeffV Nov 25 '08 at 14:56
  • 5
    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. – jalf Nov 25 '08 at 16:02
  • deleted my comment on why operator() is chosen specifically, since you edited that into your post :) – jalf Nov 25 '08 at 17:03
  • '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. – Steve Jessop Jan 20 '10 at 16:14
  • @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. – Mark Ransom Oct 27 '14 at 15:47
  • @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 – jalf Oct 28 '14 at 19:02
  • 3
    @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 – jalf Oct 28 '14 at 19:02
33

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");
Lodle
  • 31,277
  • 19
  • 64
  • 91
7

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;
};
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 6
    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 Nov 25 '08 at 14:39
  • 7
    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. – jalf Nov 25 '08 at 16:04
6

The use of operator() to form functors in C++ is related to functional programming paradigms that usually make use of a similar concept: closures.

Judge Maygarden
  • 26,961
  • 9
  • 82
  • 99
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.

carson
  • 5,751
  • 3
  • 24
  • 25
5

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

Joris Timmermans
  • 10,814
  • 2
  • 49
  • 75
4

Start using std::for_each, std::find_if, etc. more often in your code and you'll see why it's handy to have the ability to overload the () operator. It also allows functors and tasks to have a clear calling method that won't conflict with the names of other methods in the derived classes.

Michel
  • 1,456
  • 11
  • 16
3

Functors are basically like function pointers. They are generally intended to be copyable (like function pointers) and invoked in the same way as function pointers. The main benefit is that when you have an algorithm that works with a templated functor, the function call to operator() can be inlined. However, function pointers are still valid functors.

Greg Rogers
  • 35,641
  • 17
  • 67
  • 94
3

One strength I can see, however this can be discussed, is that the signature of operator() looks and behaves the same across different types. If we had a class Reporter which had a member method report(..), and then another class Writer, which had a member method write(..), we would have to write adapters if we would like to use both classes as perhaps a template component of some other system. All it would care about is to pass on strings or what have you. Without the use of operator() overloading or writing special type adapters, you couldn't do stuff like

T t;
t.write("Hello world");

because T has a requirement that there is a member function called write which accepts anything implicitly castable to const char* (or rather const char[]). The Reporter class in this example doesn't have that, so having T (a template parameter) being Reporter would fail to compile.

However, as far I can see this would work with different types

T t;
t("Hello world");

though, it still explicitly requires that the type T has such an operator defined, so we still have a requirement on T. Personally, I don't think it's too wierd with functors as they are commonly used but I would rather see other mechanisms for this behavior. In languages like C# you could just pass in a delegate. I am not too familiar with member function pointers in C++ but I could imagine you could achieve the same behaviour there aswell.

Other than syntatic sugar behaviour I don't really see the strengths of operator overloading to perform such tasks.

I am sure there are more knowingly people who have better reasons than I have but I thought I'd lay out my opinion for the rest of you to share.

Statement
  • 3,888
  • 3
  • 36
  • 45
  • 2
    The advantage of using the operator() is that your template parameter can be equally a function pointer or a functor. – Luc Touraille Nov 27 '08 at 09:02
2

Another co-worker pointed out that it could be a way to disguise functor objects as functions. For example, this:

my_functor();

Is really:

my_functor.operator()();

So does that mean this:

my_functor(int n, float f){ ... };

Can be used to overload this as well?

my_functor.operator()(int n, float f){ ... };
JeffV
  • 52,985
  • 32
  • 103
  • 124
  • 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. – altruic Nov 25 '08 at 14:23
  • 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. – eduffy Nov 25 '08 at 14:38
2

Other posts have done a good job describing how operator() works and why it can be useful.

I've recently been using some code that makes very extensive use of operator(). A disadvantage of overloading this operator is that some IDEs become less effective tools as a result. In Visual Studio, you can usually right-click on a method call to go to the method definition and/or declaration. Unfortunately, VS isn't smart enough to index operator() calls. Especially in complex code with overridden operator() definitions all over the place, it can be very difficult to figure out what piece of code is executing where. In several cases, I found I had to run the code and trace through it to find what was actually running.

Mr Fooz
  • 109,094
  • 6
  • 73
  • 101
0

Overloading operator() can make the class object calling convention easier. Functor is one of the applications of operator() overloading.

It is easy to get confused between Functor and user-defined conversion function.

Below 2 examples show the difference between

1. Functor
2. User-defined conversion function

1. Functor:

   struct A {
     int t = 0;
     int operator()(int i) { return t += i; } // must have return type or void
   };

   int main() {
     A a;
     cout << a(3); // 3
     cout << a(4); // 7 (Not 4 bcos it maintaines state!!!)
   }

2. User-defined conversion function:

    struct A {
        int t = 3;
        operator int() { return t; } // user-defined conversion function 
                                     // Return type is NOT needed (incl. void)
    };

    int main() {
        cout << A(); // 3 - converts the object{i:3} into integer 3

        A a;
        cout << a;   // 3 - converts the object{i:3} into integer 3
    }
SridharKritha
  • 8,481
  • 2
  • 52
  • 43
  • This fails to answer the question of _"why override `operator()`"_, and provides an example lifted straight from [the accepted answer](https://stackoverflow.com/a/317528/1678770) just with the names and numbers slightly changed. This does not provide any additional value over existing answers. Your second example isn't even `operator()` at all, it's a conversion operator but not a call operator and is not even applicable to the question as a whole. – Human-Compiler Apr 21 '22 at 12:53