[英]std::set select less or greater comparator at runtime
我正在重构一些代码,发现有两个地方可以使用相同的代码编写,除了一个集合的比较器在一个地方less<double>
在另一个地方greater<double>
。 就像是:
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
}
所以我想到了:
double MyClass::GeneralFunction(double val, bool condition)
{
if(condition)
{
// Select greater as comparator
}
else
{
// Select less as comparator
}
set<double, comparator> s;
// common code
}
我通过使用我的自定义比较器函数使它工作,如下所示:
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);
//....
}
但我想使用内置的。 问题是我不知道如何声明比较器并为其分配内置谓词。
任何帮助将不胜感激。
你真的需要运行时检查吗?
template <class Comp> double MyClass::Function(double val)
{
std::set<double, Comp > s;
// Do something with s
}
即使你这样做,你仍然可以使用
double MyClass::Function(double val, bool comp)
{
return comp ? Function<std::less<double> >(val) : Function<std::greater<double> >(val);
}
问题是您无法在tuntime中选择比较器的类型 ,而std::less
和std::greater
具有不相关的类型。 类似地,使用std::less
作为比较器实例化的std::set
具有与std::greater
实例化时无关的类型。 有几种可能的解决方案,但最简单的(并且唯一不涉及入侵,虚函数和动态分配的解决方案)与您正在做的事情一致:
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 );
}
};
我使用了标准的std::less
和std::greater
因为你表示有兴趣这样做。 在double
的情况下,坦率地说,这是过度的; 我通常只写d1 > d2
和d1 < d2
。 然而,上面的模板化版本可能有意义,因为某些类型可能具有专门的std::less
。 这也是我只使用std::less
; 可以想象,程序员只专注于std::less
,知道这是唯一用于标准库中的排序的程序员。
只是为了完成:显而易见的替代方案是在比较器中使用策略模式,并使用抽象比较器库:
class Comparator
{
public:
virtual ~Comparator() {}
virtual bool isLessThan( double d1, double d2 ) const = 0;
};
,用于不同比较的相当明显的派生类,以及管理内存的包装器:
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 );
}
};
这对于你需要的二元选择来说肯定是过度的,但如果有更多的选择可能是合适的; 例如,可以在许多不同字段(所有不同类型)中的一个上排序的set
。
只是用
std::set<double, std::function<bool(double,double)>>
作为你的集合,并像这样实例化:
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>() );
}
请注意,一般来说,您的权衡是:
我更喜欢第二个选项,所以你不能在使用一个集合时不小心改变顺序,打破它的所有不变量。
只是快速思考,因为这可能是一个单独的答案(甚至问题),但你提到两位代码是相同的,除了排序顺序。
我在某些情况下使用的替代方法是使用单个排序方向,并模拟在集合上运行的代码(通过迭代器类型),这样你就可以
if (increasing)
do_stuff(set.begin(), set.end());
else
do_stuff(set.rbegin(), set.rend());
为什么不呢
template <typename Compare>
double MyClass::GeneralFunction(double val)
{
std::set<double, Compare> s;
//....
}
通过形式参数选择模板不是C ++处理得很好的东西。 通过让调用者提供模板参数,尽可能多地进入编译阶段。
然后你可以提供一个包装器,如果你真的想在运行时选择一个:
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.