7

I recently have got excited by functors and been using them all over the place. Then the situation arose where I needed my functor to perform two different operations and I thought about adding another method to my functor (not overloading the () operator). Whether this is bad practice or not I am not sure (perhaps you could tell me), but it got me thinking about why I am using functors in the first place and not just objects. So my question is:

Is there anything special about overloading the () operator or is it just very slightly more syntactically appealing than using normal named methods?

Update:

Firstly, I know why functors may be preferable to function pointers as explained in other questions. I want to know why they can be preferable to objects with named methods.

Secondly, as for an example of when I wanted to use another possibly named method of my functor: Basically I have two functions, one which calculates something called the modularity of a graph partition - compute_modularity(), and another which computes the gain in modularity after some change of the partitioncompute_modularity_gain(). I thought I could pass these functions as part of the same functor into an optimisation algorithm, with the gain as a named function. The reason I don't just pass two functors into the algorithm, is that I want to enforce that compute_modularity_gain() is used only in conjuction with compute_modularity() and not another functor e.g. compute_stability() (which should only be used with compute_stability_gain(). In other words, the gain function must be tightly coupled with its sibling function. If there is another way I can enforce this constraint then please let me know.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
zenna
  • 9,006
  • 12
  • 73
  • 101
  • Describe the scenario that made you want to add another method. – Marcelo Cantos Apr 05 '11 at 00:45
  • 4
    This seems to have been covered: [C++ Functors - and their uses.](http://stackoverflow.com/q/356950/478288) – chrisaycock Apr 05 '11 at 00:46
  • 4
    @chrisaycock: You should only vote close *exact* duplicates. This question is similar, but has a qualitatively different aspect: why can't functors be implemented using normal named methods and why can they only implement the one method? – Marcelo Cantos Apr 05 '11 at 00:52
  • @chrisaycock I have seen that question, it does need to seem to answer my question, unless I have missed something. I understand why a functor may be preferable over a function pointer, I want to know it is preferable over just a normal object using normal methods as opposed to operator overloading – zenna Apr 05 '11 at 01:26
  • @zenna: You can never *enforce* such a constraint. Nothing stops the caller from creating and passing a class that implements `compute_modularity_gain()` correctly and makes bunny rabbits jump out of your screen when you call `compute_modularity()`. The best you can do is make it so that when they do the wrong thing, it looks wrong. E.g., The member function that accepts the modularity-related functions could be called setModularityFunctors(functor1, functor2), thus a stability-related functor would look completely out of place if passed to this member function. – Marcelo Cantos Apr 05 '11 at 01:45

3 Answers3

7

The reason for the overload of operator() is to make functors have the same call semantics as function pointers -- and in fact you can use function pointers if you so desire.

There are a few reasons for overloading operator() instead of using a function -- but the most important one is that compilers will rarely optimize away the indirect function call when using a function pointer, but they'll almost always optimize away the operator() call -- this is why std::sort usually beats std::qsort.

There are a whole bunch of complicated reasons for this but what it really boils down to is that most (no?) compilers implement the optimization possible of removing the function pointer call, which is expensive on modern hardware.

Then the situation arose where I needed my functor to perform two different operations

Then it's no longer a functor. Either pass two functors to do what you want, or define a template method class. (You could also use mixins to achieve the template method pattern in C++ without runtime overhead -- but the Wikipedia article doesn't cover this) (Note also: Not the same as C++ templates, though C++ templates can be involved if you go the AOP route)

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
1

The basic intent behind a functor is to decouple the code that knows how to perform some kind of work from the code that knows when that work needs to be done (the classic example is associating a functor with a UI button).

One minor benefit of the functor model is that plain old function pointers are already functors. No extra work is required to wrap them. I consider this a minor benefit because a) a function pointer is slightly less efficient than wrapping a direct call to the function, and b) I find that I almost always need to bind some form of state to whatever I'm wrapping, even if it's just the this pointer of a member function.

The key advantage of a unary interface is that it serves as a lingua franca for producers and consumers of functors. You could, say, define functors to all have an invoke() member function, but then some other crowd would decide to standardise on do(), and yet another might go for call(). And all of these solutions involve more typing.

Also, multiple member functions on a single "functor" are never strictly required. If some code needs to invoke multiple distinct operations, you can simply pass multiple functors. This provides good flexibility, since the operations may be coupled, or they may be completely unrelated.

A decoupled example is a hash table that needs an equality-comparator and a hash function. In this case, the two functions could be unrelated: wrap the class's operator==() for equality and wrap a free function to compute the hash.

A coupled example is a UI component that emits several distinct events. A single class might respond to all the events, or different classes might respond to different groups of events. Functors make it easy to choose either model, whereas requiring a single "interface" that defines callbacks for all the component's events is more awkward. Functors also make it much easier if a single object wants to handle events from two components differently, since you can give each component a different set of functor-wrapped member functions.

Finally, wrapping existing functionality in a functor is well understood and widely supported by libraries such as boost.bind, whereas creating throw-away classes that implement doX() and doY() isn't. Plus, the new standard adds lambdas, which dramatically simplify the creation of functors.

Marcelo Cantos
  • 181,030
  • 38
  • 327
  • 365
0

The only thing special about functors is that they can be used in the same way as functions. However functors may also have information injected through their constructor.

You might also want to look into std::function (or boost::function if your compiler does not support it yet) that can be used to adapt any type of object with a matching call signature.

std::bind or boost::bind lets you associate concrete arguments with a function's parameters, this allows for the same effect as passing them in through the constructor of a functor. You can even use bind to provide the this pointer to member functions so that they can be called in the same way as a plain functor, without specifying an object explicitly.

xDD
  • 3,443
  • 1
  • 15
  • 12
  • "may also have information injected through their copy constructor" <-- Well, not their copy constructor. Just the normal constructor. Also keep in mind that functors must be [pure functions](http://en.wikipedia.org/wiki/Pure_function) -- you should not store state in STL functors because the STL is allowed to copy your functor arbitrarily and as many times as it wants. – Billy ONeal Apr 05 '11 at 01:36
  • Yep no idea how I ended up typing copy constructor while meaning the normal constructor, edited. ;o – xDD Apr 05 '11 at 01:53