[英]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.