2012-06-25 79 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可能是 排序在许多不同的领域之一(所有不同类型)。