2012-06-25 148 views
5

我重構了一些代碼,發現有兩個地方可以使用相同的代碼編寫,除了一個地方的比較器是less<double>而另一個地方是greater<double>。喜歡的東西:std :: set在運行時選擇比較小或更大的比較器

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); 

    //.... 
} 

但我想使用內置的。問題是我不知道如何聲明比較器併爲它分配內置的謂詞。

任何幫助將不勝感激。

回答

4

你真的需要一個運行時檢查?

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); 
} 
+0

謝謝!我真的不需要運行時檢查,所以我可以在調用者中選擇比較器。 – MikMik

2

爲什麼不通過正式的參數做

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);\ 
} 
3

只需使用

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()); 
+0

+1用於'std :: function'。關於集合在使用時偶然改變順序的風險:任何標誌應該是功能對象的私有成員,這大大降低了風險。 –

+0

std :: function部分就是我真正要求的,但是我認爲MSalters的答案是一個更好的解決方案。 – MikMik

4

的問題在於你不能選擇類型比較器在 tuntime和std::lessstd::greater有不相關的類型。 類似地,作爲比較器 與std::less實例化的std::set具有與用std::greater實例化時無關的類型。有幾個 可能的解決方案,但最簡單的(也是唯一一個沒有 涉及inhertance,虛函數和動態分配)是你在做什麼沿 行:

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::lessstd::greater,因爲你 表示有興趣這樣做。在double的情況下,坦率地說,這是, ,矯枉過正;我通常只寫d1 > d2d1 < 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可能是 排序在許多不同的領域之一(所有不同類型)。