Search code examples
c++oopfunctordesign-guidelines

Pros and Cons of implementing functionality as functor


I'm having a discussion with a colleague about the API of a simple class that has only one public method. I initially went for:

class CalculateSomething
{
public:
  void operator()(const SomeObject &obj) const;

private:
  // ...
}

However, my colleague opposes the use of operator() and wishes to simply name the method 'calculate' for the sake of clarity. Although I don't find that argument convincing, it made me think of the pros and cons.

Advantages operator()

  • The class is lean and has a single well-defined purpose. Once instantiated, it basically acts as a free function.
  • It is a functor and can be easily used as such (e.g. STL algorithms).
  • When using it with range algorithms, the object can be passed directly instead of through a function pointer, which enables the compiler to inline the code. Although not guaranteed, passing it through a function pointer completely inhibits this possibility.

Disadvantages operator()

  • It is less clear what the method does without looking at the class name. (I personally disagree as the class has only one method and its meaning is therefore clear from the class name)
  • Most functors in the STL are expected to be stateless. This I think is the main reason that's holding me back...

I was surprised to see that my search on this didn't bring up much since I would assume that this is quite a common scenario (one class, one responsibility). As such, I'm really interested in hearing what other people think about this.


Solution

  • If lambdas are really not an option, Your choice should depend on the scope of work that object undertakes... and of cause your coding conventions or styles. You can decide to be explicit (see Werolik's answer), its a good thing if the method is relatively unfamiliar and requires states, but

    Let us take simple use-cases from the standard library...

    • std::hash: This is a function object that does one job, and does it supposedly well, no contest about that
    • so many more... including std::less, and its assortments

    One thing you see in common with all these is that they are verbs... In my perspective, if your class is exactly like the snippet you posted, CalculateSomething signifies an action to me, so I can always instantiate it as CalculateSomething()(my_object...).

    And like you cited, it comes in very handy in using STL algorithm itself and many other C++ libraries. If you go your colleague's method, you may have to resort to using std::binds and lambdas because you want to 'adapt' an interface.

    Example:

    class CalculateSomething
    {
        public:
             void operator()(const SomeObject &obj) const;
        private:
             // ...
    }
    
    class CalculateNothing
    {
        public:
             void calculate(const SomeObject &obj) const;
        private:
             // ...
    }
    

    An example usage is:

    std::for_each(container.begin(), container.end(), CalculateSomething());
    

    against

    std::for_each(container.begin(), container.end(), [ c = CalculateNothing()](auto x) { c.calculate(x); });
    

    I think I prefer the former.