您如何构建比较功能?

发布于 2024-07-13 22:22:49 字数 650 浏览 6 评论 0原文

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

I frequently encounter situations, especially with sorting in C++, where I am comparing a series of fields in order to compare a larger structure. A simplified example:

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;
}

My instinctive approach seems cumbersome, especially for more than 3 fields. How would you structure this series of comparisons in C++? Do other languages provide a more succinct or elegant syntax?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

初见你 2024-07-20 22:22:50

就我个人而言,我建议不要像我们在这里推荐的那样使用 != 或 == 运算符 - 这要求参数/成员同时具有 less then 和 equal 运算符,只是为了对包含它们的类进行 less then 检查 - 仅使用less then 运算符就足够了,并且可以为您节省冗余和将来潜在的缺陷。

我建议你写:

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;
}

Personally I'd suggest NOT using the != or == operators like we seem to recommend here - this requires the arguments/members to have both less then and equal operators just to do a less then check on a class containing them - using just the less then operator is enought and will save you redundancy and potential defects in the future.

I suggest you write:

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;
}
舞袖。长 2024-07-20 22:22:50

我知道这是一个老问题,但对于未来的访问者:现代 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 函数。

I know it's an old question, but for future visitors: the modern C++11 solution is to use 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 converts the struct into a std::tuple so that the above comparison operator delegates to std::tuple::operator<. This in turn does a lexicographical compare with respect to the order in which the members are marshalled into std::tie.

The lexicographic comparison is short-circuited in the same way as in the other solutions to this question. But it is even succinct enough to define on the fly inside a C++ lambda expression. For classes with private data members, it's best defined inside the class as friend function.

时间你老了 2024-07-20 22:22:50
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  ) 
      )));

——马库斯Q

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

紫轩蝶泪 2024-07-20 22:22:50

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

Personally, I'd override the ==, <, >, and any other operators needed. That would clean up the code, not in the comparison, but when you need to make the comparison.
For the actual comparison itself, I would write it similarly to what Crashworks said.

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;
}
盛夏尉蓝 2024-07-20 22:22:50

我想知道与OP相同的事情并偶然发现了这个问题。 阅读答案后,我受到 janm 和 RnR 的启发,编写了一个 lexicographyalMemberCompare 模板函数,该函数仅在比较成员上使用 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;
}

希望这对某人有用。

I was wondering the same thing as the OP and stumbled upon this question. After reading the answers I have been inspired by janm and RnR to write a lexicographicalMemberCompare template function that only uses only operator< on the compared members. It also uses boost::tuple so that you can specify as many members as you want. Here it is:

#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;
}

Hope this is useful to someone.

梅倚清风 2024-07-20 22:22:49

好吧,如果你的函数在 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 短路方法。

Well, if your function hits a return in the if clause, there's no need for an explicit else, since it would have already bailed out. That can save on the "indent valley":

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;
}

I like MarkusQ's LISPish short-circuiting approach as well.

早乙女 2024-07-20 22:22:49

如果这种情况经常发生,您可以将这样的模板放入公共标头中:

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);
}

If this happens a lot you could put a template like this into a common header:

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);
}

Add other templates for different numbers of arguments as required. For each less than function, you can then do something like this:

bool carLessThanComparator(const Car& car1, const Car& car2)
{
    return do_less_than(car1, car2, &Car::make, &Car::model, &Car::year);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文