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