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);
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.
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.
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).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With