你如何构建比较函数?

时间:2009-02-28 05:10:21

标签: c++ coding-style language-features lexicographic stdtuple

我经常遇到情况,特别是在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 ++中构建这一系列的比较?其他语言是否提供了更简洁或优雅的语法?

7 个答案:

答案 0 :(得分:5)

好吧,如果你的函数在 if 子句中返回,则不需要显式的 else ,因为它已经被拯救了。这可以节省“缩进谷”:

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短路方法。

答案 1 :(得分: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);
}

答案 2 :(得分: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;
}

答案 3 :(得分: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函数。

答案 4 :(得分: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

答案 5 :(得分:2)

就个人而言,我会覆盖==,&lt;,&gt;以及所需的任何其他运算符。这将清理代码,而不是在比较中,但是当你需要进行比较时。 对于实际比较本身,我会像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;
}

答案 6 :(得分: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;
}

希望这对某人有用。