简体   繁体   English

std :: set在运行时选择更少或更大的比较器

[英]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::lessstd::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::lessstd::greater因为你表示有兴趣这样做。 In the case of double , this is, frankly, overkill; double的情况下,坦率地说,这是过度的; I'd normally just write d1 > d2 and d1 < d2 . 我通常只写d1 > d2d1 < 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: 请注意,一般来说,您的权衡是:

  • check ordering on every comparison, or 检查每次比较的排序,或
  • check it once at instantiation but then also incur an indirection on every function call (like a virtual function call for example) 在实例化时检查一次但是在每次函数调用时也会产生间接(例如虚函数调用)

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM