I was refactoring some code and found there are two places that can be written with the same code except the comparator of a set is less<double>
in one place and greater<double>
in the other. Something like:
double MyClass::Function1(double val)
{
std::set<double, less<double> > s;
// Do something with s
}
double MyClass::Function2(double val)
{
std::set<double, greater<double> > s;
// Do the same thing with s as in Function1
}
So I thought of doing:
double MyClass::GeneralFunction(double val, bool condition)
{
if(condition)
{
// Select greater as comparator
}
else
{
// Select less as comparator
}
set<double, comparator> s;
// common code
}
I've made it work by using my custom comparator functions, like this:
bool my_greater(double lhs, double rhs)
{
return lhs > rhs;
}
bool my_less(double lhs, double rhs)
{
return lhs < rhs;
}
double MyClass::GeneralFunction(double val, bool condition)
{
typedef bool(*Comparator) ( double, double);
Comparator comp = &my_less;
if (condition)
{
comp = &my_greater;
}
std::set<double, Comparator > s(comp);
//....
}
But I would like to use the built-in ones. The problem is I don't know how to declare the comparator and assign it the built in predicates.
Any help would be greatly appreciated.
auto cmp = [](int a, int b) { return ... }; std::set<int, decltype(cmp)> s; We use lambda function as comparator. As usual, comparator should return boolean value, indicating whether the element passed as first argument is considered to go before the second in the specific strict weak ordering it defines.
A Set stores the elements in sorted order. Set stores unique elements. Elements can only be inserted and deleted but cannot be modified within the set.
Sets are a type of associative container in which each element has to be unique because the value of the element identifies it. The values are stored in a specific sorted order i.e. either ascending or descending.
Per the C++ standard, iteration over the elements in an std::set proceeds in sorted order as determined by std::less or by the optional comparison predicate template argument.
Do you really need a runtime check?
template <class Comp> double MyClass::Function(double val)
{
std::set<double, Comp > s;
// Do something with s
}
Even if you do, you can still use
double MyClass::Function(double val, bool comp)
{
return comp ? Function<std::less<double> >(val) : Function<std::greater<double> >(val);
}
The problem is that you cannot choose the type of the comparator at
tuntime, and std::less
and std::greater
have unrelated types.
Similarly, an std::set
instantiated with std::less
as a comparator
has a type unrelated to on instantiated with std::greater
. There are
several possible solutions, but the simplest (and the only one not
involving inhertance, virtual functions and dynamic allocation) is along
the lines of what you are doing:
class SelectableCompare
{
bool myIsGreater;
public:
SelectableCompare( bool isGreater ) : myIsGreater( isGreater ) {}
bool operator()( double d1, double d2 ) const
{
static std::less<double> const less;
return myIsGreater
? less( d2, d1 )
: less( d1, d2 );
}
};
I've used the standard std::less
and std::greater
because you
expressed an interest in doing so. In the case of double
, this is,
frankly, overkill; I'd normally just write d1 > d2
and d1 < d2
. A
templated version of the above, however, might make sense, since some
types might have a specialized std::less
. This is also why I only use
std::less
; it's quite conceivable that a programmer specialize only
std::less
, with the knowledge that this is the only one used for
ordering in the standard library.
Just to be complete: the obvious alternative is to use the strategy pattern in the comparator, with an abstract comparator base:
class Comparator
{
public:
virtual ~Comparator() {}
virtual bool isLessThan( double d1, double d2 ) const = 0;
};
, the rather obvious derived classes for the different comparisons, and a wrapper to manage the memory:
class ComparatorWrapper
{
std::shared_ptr<Comparator> myComparator;
public:
ComparatorWrapper( Comparator* newed_comparator )
: myComparator( newed_comparator )
{
}
bool operator()( double d1, double d2 ) const
{
return myComparator->isLessThan( d1, d2 );
}
};
This is definitely overkill for the binary choice you need, but might be
appropriate if there were more choices; e.g. a set
which might be
sorted on one of many different fields (all of different types).
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