簡體   English   中英

std :: set在運行時選擇更少或更大的比較器

[英]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::lessstd::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::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

只是用

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM