Search code examples
c++operator-overloadingfunctor

When are stateless class functors useful in place of a c style function?


I've found some good examples of functors on SO like this one, and all the convincing examples seem to use state in the class that defines operator().

I came across an example in a book that defines the function call operator without having state, and I can't help but feel like this is an awkward usage, and that a normal style function pointer, would be better than using operator() in every way here - less code, less variables (you have to instantiate the comparators), its probably more efficient due to the instantiation, and no loss of meaning or encapsulation (since it's just one function).

I know std::sort lets you pick between operator() classes and functions, but I've always just used the functions because of the above logic.

What are the reasons why a class might be preferred?

Here's the example (paraphrased):

class Point2D {
   //.. accessors, constructors
   int x,y;
};
class HorizComp {
public:
   bool operator()(const Point2D& p, const Point2D& q) const
   { return p.getX() < q.getX(); }
};

class VertComp {
public:
   bool operator()(const Point2D& p, const Point2D& q) const
   { return p.getY() < q.getY(); }
};

template <typename E, typename C>
void printSmaller(const E& p, const E& q, const C& isLess) {
   cout << (isLess(p, q) ? p : q) << endl; // print the smaller of p and q
}
//...
// usage in some function:
Point2D p(1.2, 3.2), q(1.5, 9.2);
HorizComp horizComp;
VertComp vorizComp;
printSmaller(p, q, horizComp);
printSmaller(p, q, vorizComp);

Solution

  • The typical reason is that when you do this:

    bool less_than(const Point&, const Point&);
    // ...
    std::sort(..., &less_than);
    

    The template argument for the predicate is the following:

    bool(const Point&,const Point&)
    

    Since the sort function receives a function pointer, it is more difficult for the compiler to inline the predicate use inside std::sort(). This happens because you could have another function

    bool greater_than(const Point&, const Point&);
    

    which has the exact same type, meaning the std::sort() instatiation would be shared between the two predicates. (remember that I said that it makes inlining more difficult, not impossible).

    In contrast, when you do this:

    struct less_than {
        bool operator()(const Point&, const Point&) const;
    };
    // ...
    std::sort(..., less_than());
    
    
    struct greater_than {
        bool operator()(const Point&, const Point&) const;
    };
    // ...
    std::sort(..., greater_than());
    

    The compiler generates a unique template instantiation for std::sort() for each predicate, making it easier to inline the predicate's definition.