2009-02-28 41 views
4

我经常遇到这样的情况,特别是在用C++进行排序时,我正在比较一系列字段,以便比较更大的结构。一个简单的例子:你如何构建比较函数?

struct Car{ 
    Manufacturer make; 
    ModelName model; 
    Year year; 
}; 

bool carLessThanComparator(const Car & car1, const Car & car2){ 
    if(car1.make < car2.make){ 
     return true; 
    }else if(car1.make == car2.make){ 
     if(car1.model < car2.model){ 
      return true; 
     }else if(car1.model == car2.model){ 
      if(car1.year < car2.year){ 
       return true; 
      } 
     } 
    } 

    return false; 
} 

我本能的方法似乎很麻烦,特别是对于超过3个领域。你将如何在C++中构建这一系列比较?其他语言是否提供更简洁或优雅的语法?

+0

个人风格​​ - 我会实现一个`strcmp()`函数(或方法),如果a等于b则返回0,如果a大于b则返回1,如果a小于b则返回-1,然后返回根据比较函数定义比较运算符(如果您喜欢)和/或方法。然后,我们只需要`bool carLessThanComparator(const Car&car1,const Car&car2){return cmp(car1,car2)== -1; }` – 2010-02-04 07:06:38

回答

5

好吧,如果你的函数打如果子句中的回报,就没有必要明确地其他,因为它已经保释出来。这可以节省“缩进谷”:

bool carLessThanComparator(const Car & car1, const Car & car2) { 
    if(car1.make < car2.make) 
     return true; 

    if (car1.make != car2.make) 
     return false; 

    if(car1.model < car2.model) 
     return true; 

    if(car1.model != car2.model) 
     return false; 

    if(car1.year < car2.year) 
     return true; 

    return false; 
} 

我喜欢MarkusQ的LISPish短路方法。

+0

为什么不把“car1.year janm 2009-02-28 05:57:29

+1

只是为了对称 - 我希望代码示例能够尽可能清晰。在实践中,我会按照你所说的去做,或者我真的会像马库斯那样对它进行编码。 – Crashworks 2009-02-28 06:02:11

+0

非常感谢。人们需要从中学习。 – ChaosPandion 2013-03-30 21:36:16

2
bool carLessThanComparator(const Car & car1, const Car & car2){ 
    return (
     (car1.make < car2.make ) or ((car1.make == car2.make ) and 
     (car1.model < car2.model) or ((car1.model == car2.model) and 
     (car1.year < car2.year ) 
    ))); 

- MarkusQ

4

如果出现这种情况有很多,你可以把这样的一个模板到一个共同的标题:

template<typename T, typename A1, typename A2, typename A3> 
bool 
do_less_than(
     const typename T& t1, 
     const typename T& t2, 
     const typename A1 typename T::* a1, 
     const typename A2 typename T::* a2, 
     const typename A3 typename T::* a3) 
{ 
    if ((t1.*a1) < (t2.*a1)) return true; 
    if ((t1.*a1) != (t2.*a1)) return false; 
    if ((t1.*a2) < (t2.*a2)) return true; 
    if ((t1.*a2) != (t2.*a2)) return false; 
    return (t1.*a3) < (t2.*a3); 
} 

不同数量的参数添加其他模板的要求。对于除各功能少,你就可以做这样的事情:

bool carLessThanComparator(const Car& car1, const Car& car2) 
{ 
    return do_less_than(car1, car2, &Car::make, &Car::model, &Car::year); 
} 
+0

我很少看到C++中的指针 - 成员语法。很有意思。 – jasonmray 2009-02-28 06:24:12

+0

这就是为什么 – 2009-02-28 10:57:01

2

就个人而言,我会重写==,<,>,以及所需的任何其他运营商。这将清理代码,而不是比较,但是当你需要进行比较时。 对于实际的比较本身,我会写类似于Crashworks所说的。

bool operator<(const Car &car1, const Car &car2) { 
    if(car1.make < car2.make) 
     return true; 
    if(car1.make != car2.make) 
     return false; 
    if(car1.model < car2.model) 
     return true; 
    if(car1.model != car2.model) 
     return false; 
    return car1.year < car2.year; 
} 
4

个人而言,我会建议不使用=或==像我们运营商似乎在这里推荐 - !这需要的参数/成员既有小于等于和运营商只是做一个不太然后检查一个包含它们的类 - 仅使用少于一个的操作符就足够了,并且将为您节省冗余和未来潜在的缺陷。

我建议你写:

bool operator<(const Car &car1, const Car &car2) 
{ 
    if(car1.make < car2.make) 
     return true; 
    if(car2.make < car1.make) 
     return false; 

    if(car1.model < car2.model) 
     return true; 
    if(car2.model < car1.model) 
     return false; 

    return car1.year < car2.year; 
} 
1

我想知道的事情是一样的OP和偶然发现了这个问题。在阅读答案后,我受到了janm和RnR的启发,编写了一个lexicographicalMemberCompare模板函数,该函数仅使用比较成员中的operator<。它还使用boost::tuple,以便您可以指定尽可能多的成员。这里是:

#include <iostream> 
#include <string> 
#include <boost/tuple/tuple.hpp> 

template <class T, class Cons> 
struct LessThan 
{ 
    static bool compare(const T& lhs, const T& rhs, const Cons& cons) 
    { 
     typedef LessThan<T, typename Cons::tail_type> NextLessThan; 
     typename Cons::head_type memberPtr = cons.get_head(); 
     return lhs.*memberPtr < rhs.*memberPtr ? 
      true : 
      (rhs.*memberPtr < lhs.*memberPtr ? 
       false : 
       NextLessThan::compare(lhs, rhs, cons.get_tail())); 
    } 
}; 

template <class T> 
struct LessThan<T, class boost::tuples::null_type> 
{ 
    static bool compare(const T& lhs, const T& rhs, 
         const boost::tuples::null_type& cons) 
    { 
     return false; 
    } 
}; 

template <class T, class Tuple> 
bool lexicographicalMemberCompare(const T& lhs, const T& rhs, 
            const Tuple& tuple) 
{ 
    return LessThan<T, typename Tuple::inherited>::compare(lhs, rhs, tuple); 
} 

struct Car 
{ 
    std::string make; 
    std::string model; 
    int year; 
}; 

bool carLessThanCompare(const Car& lhs, const Car& rhs) 
{ 
    return lexicographicalMemberCompare(lhs, rhs, 
     boost::tuples::make_tuple(&Car::make, &Car::model, &Car::year)); 
} 

int main() 
{ 
    Car car1 = {"Ford", "F150", 2009}; 
    Car car2 = {"Ford", "Escort", 2009}; 
    std::cout << carLessThanCompare(car1, car2) << std::endl; 
    std::cout << carLessThanCompare(car2, car1) << std::endl; 
    return 0; 
} 

希望这对某人有用。

3

我知道这是一个老问题,但对于未来的用户:现代C++ 11的方案是使用std::tie

struct Car{ 
    Manufacturer make; 
    ModelName model; 
    Year year; 
}; 

bool operator<(Car const& lhs, Car const& rhs) 
{ 
    return std::tie(lhs.make, lhs.model, lhs.year) < std::tie(rhs.make, rhs.model, rhs.year); 
} 

std::tie的结构转换为std::tuple,这样上面的比较操作委托给std::tuple::operator< 。这反过来对成员编组到std::tie的顺序做了词典比较。

词典对比短路的方式与此问题的其他解决方案相同。但它足够简洁,可以在C++ lambda表达式中快速定义。对于具有私有数据成员的类,它最好在类中定义为friend函数。