I'll try to answer the question why a functor is used. It's just a guess of course, as I'm not the author of the code, but I saw discussions about it at least a few times and the consensus seems to be, that functors enable or at least make it easier to inline the comparison code.
Functors are structs (or classes) and in general are more flexible than regular functions because they can have some members, that store some state, which can be used by operator(). In this case this advantage isn't used, so the functor was most probably used to enable (or help) in inlining or just because the author was used to this common pattern.
Why would it help in inlining? Let's look at a simple example. Lets take std::sort
template <class RandomAccessIterator, class Compare>
void sort ( RandomAccessIterator first, RandomAccessIterator last, Compare comp );
Imagine You want to sort std::vector<int>
and You want to provide Your custom comparators.
struct MyStructComp1
{
bool operator()(int lhs, int rhs) const { /*...*/}
};
struct MyStructComp2
{
bool operator()(int lhs, int rhs) const { /*...*/}
};
bool myFunctComp1 (int lhs, int rhs) const { /*...*/}
bool myFunctComp2 (int lhs, int rhs) const { /*...*/}
Now You can use the sort
temeplate in the following ways
sort(myvector.begin(), myvector.end(), MyStructComp1()); // 1
sort(myvector.begin(), myvector.end(), MyStructComp2()); // 2
sort(myvector.begin(), myvector.end(), myFunctComp1); // 3
sort(myvector.begin(), myvector.end(), myFunctComp2); // 4
Here are the function the compiler creates form the template
sort<vector<int>::iterator, MyStrucComp1> // 1
sort<vector<int>::iterator, MyStrucComp2> // 2
sort<vector<int>::iterator, bool (*) (int lhs, int rhs)> // 3, // 4
Since the Compare parameter in the sort template is a type, and functors are types, the compiler creates a different function for every functor supplied as a template argument. sort<vector<int>::iterator, MyStrucComp1>
and
sort<vector<int>::iterator, MyStrucComp2>
are two different functions. So when sort<vector<int>::iterator, MyStrucComp1>
is created, it is known exactly what the comparing code is and the comparator can be simply inlined.
Functions myFunctComp
1 and myFunctComp2
however are of exactly the same type:
bool (*) (int lhs, int rhs)
and the compiler creates one function sort<vector<int>::iterator, bool (*) (int lhs, int rhs)>
for all comparing functions of type bool (*) (int lhs, int rhs)
. I saw opinions, that inlining is possible anyway in this situation, but I have no idea how.
It is possible to create templates with a pointer to function as a template parameter as it's a compile time constant, but it's ugly and constants can't be deduced from the function arguments. For example if sort
was defined as:
template <class RandomAccessIterator,
bool (*comparer) (typename RandomAccessIterator::value_type, typename RandomAccessIterator::value_type)>
void sort ( RandomAccessIterator first, RandomAccessIterator last) {/* */}
You would have to call it like this
sort<std::vector<int>::iterator, myFunctComp1>(myvector.begin(), myvector.end());
You would get a different sort for every comparing function, but functors are much more convenient.