[英]std::set select less or greater comparator at runtime
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. 我正在重构一些代码,发现有两个地方可以使用相同的代码编写,除了一个集合的比较器在一个地方
less<double>
在另一个地方greater<double>
。 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. 任何帮助将不胜感激。
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. 问题是您无法在tuntime中选择比较器的类型 ,而
std::less
和std::greater
具有不相关的类型。 Similarly, an std::set
instantiated with std::less
as a comparator has a type unrelated to on instantiated with std::greater
. 类似地,使用
std::less
作为比较器实例化的std::set
具有与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. 我使用了标准的
std::less
和std::greater
因为你表示有兴趣这样做。 In the case of double
, this is, frankly, overkill; 在
double
的情况下,坦率地说,这是过度的; I'd normally just write d1 > d2
and d1 < d2
. 我通常只写
d1 > d2
和d1 < d2
。 A templated version of the above, however, might make sense, since some types might have a specialized std::less
. 然而,上面的模板化版本可能有意义,因为某些类型可能具有专门的
std::less
。 This is also why I only use std::less
; 这也是我只使用
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. 可以想象,程序员只专注于
std::less
,知道这是唯一用于标准库中的排序的程序员。
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; 这对于你需要的二元选择来说肯定是过度的,但如果有更多的选择可能是合适的; eg a
set
which might be sorted on one of many different fields (all of different types). 例如,可以在许多不同字段(所有不同类型)中的一个上排序的
set
。
Just use 只是用
std::set<double, std::function<bool(double,double)>>
as your set, and instantiate it like so: 作为你的集合,并像这样实例化:
typedef std::set<double, std::function<bool(double,double)> > RTSet;
RTSet choose_ordering(bool increasing)
{
if (increasing)
return RTSet( std::less<double>() );
else
return RTSet( std::greater<double>() );
}
Note in general your tradeoff is to either: 请注意,一般来说,您的权衡是:
I'm preferring the second option so you can't accidentally change the ordering while a set is in use, breaking all its invariants. 我更喜欢第二个选项,所以你不能在使用一个集合时不小心改变顺序,打破它的所有不变量。
Just a quick thought, as this could potentially be a separate answer (and even question), but you mention two bits of code are identical except for sort order. 只是快速思考,因为这可能是一个单独的答案(甚至问题),但你提到两位代码是相同的,除了排序顺序。
An alternative I've used in some situations, is to use a single sort direction, and template the code operating on the set (by iterator type), so you can do 我在某些情况下使用的替代方法是使用单个排序方向,并模拟在集合上运行的代码(通过迭代器类型),这样你就可以
if (increasing)
do_stuff(set.begin(), set.end());
else
do_stuff(set.rbegin(), set.rend());
Why not do 为什么不呢
template <typename Compare>
double MyClass::GeneralFunction(double val)
{
std::set<double, Compare> s;
//....
}
Template selection by formal parameter is not something C++ handles very well. 通过形式参数选择模板不是C ++处理得很好的东西。 Push as much as possible into the compilation phase by having the caller supply the template argument.
通过让调用者提供模板参数,尽可能多地进入编译阶段。
Then you can provide a wrapper, if you really want to select one at runtime: 然后你可以提供一个包装器,如果你真的想在运行时选择一个:
double MyClass::GeneralFunction(double val, bool condition)
{
return condition ?
GeneralFunction<std::greater<double> >(val) :
GeneralFunction<std::less <double> >(val);\
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.