Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I use functions or stateless functors?

These 2 piece of code do same thing. And it will be used in sort function as you can see. Which is better? I usually write latter one. But I saw some coders do it like former one.

struct val_lessthan : binary_function<pair<string,int>, pair<string, int>, bool>
{
    bool operator() (const pair<string,int>& x, const pair<string,int>& y) const
    {
        return x.second < y.second;
    }
} val_lt;

and

bool val_lt(const pair<string,int>& x, const pair<string,int>& y) 
{
    return x.second < y.second;
}

Will use it like:

std::sort(wordvector.begin(), wordvector.end(), val_lt);
like image 424
Josh Morrison Avatar asked Apr 02 '11 07:04

Josh Morrison


4 Answers

The reason you see some people prefer the first version is that functors can be trivially inlined.

When you pass a functor to std::sort, the functor type is known to the function, and so the exact function to call is also known at compile-time, and can be trivially inlined.

With a plain function, what std::sort really sees is just a function pointer, and at compile-time that says nothing about which function it points to. So that can't be inlined unless the compiler performs some fairly extensive flow analysis to see where the pointer came from in this specific call. And it will certainly do that optimization in a small example like yours, but if the functor/function pointer was passed in as a function argument from somewhere else, for example, or it was read from an intermediate data structure before being passed to std::sort, then, the compiler might not be able to inline the function pointer version, and so it would end up slower.

like image 191
jalf Avatar answered Oct 21 '22 08:10

jalf


The first one is called a function object and is useful if you need to pass any context information to the comparison function. The standalone function only gets x and y and doesn't have the opportunity to carry along any context.

In the specific instance above, the two ways of writing the comparison function are roughly equivalent.

like image 38
Greg Hewgill Avatar answered Oct 21 '22 07:10

Greg Hewgill


I'd probably prefer the first as a rule, but would generally prefer to use a template:

template <class T>
struct val_lessthan : binary_function<pair<pair<T, T>, bool> {
    bool operator()(T const &x, T const &y) const { 
       return x.second < y.second;
    }
};

Use of .second limits the degree of genericity, but you still get a little (e.g., if memory serves, boost::tuple provides a .first and .second for tuples of two elements. As a rule, being a template gives a little better assurance that the compiler will be able to generate the code inline, so if you care about efficiency, it might help a little (or it might not, but is unlikely to ever cause any harm).

like image 27
Jerry Coffin Avatar answered Oct 21 '22 09:10

Jerry Coffin


If you want to be able to also call the function in other part of your code, and not passed as a functor, prefer the function form. For example you would prefer:

if (val_lt(a,b))
{
//...
}

to

if(val_lessthan()(a,b))
{
// ...
}

Otherwise when choosing the functor form, you'd better call with an unnamed functor object. That is:

std::sort(wordvector.begin(), wordvector.end(), val_lesstthan());

instead of:

val_lesstthan named;
std::sort(wordvector.begin(), wordvector.end(), named);

Unnaming parameters and return values easily enables the compiler to perform optimization. It refers to a global concept known as RVO (Return Value Optimization). In that case it will probably free your code from one copy construction.

like image 23
yves Baumes Avatar answered Oct 21 '22 09:10

yves Baumes