定义类元素的字典比较的最简单方法是什么?

问题描述 投票:0回答:6

如果我有一个类,我希望能够排序(即支持小于概念),并且它有几个数据项,因此我需要进行字典顺序排序,那么我需要这样的东西:

struct MyData {
  string surname;
  string forename;

  bool operator<(const MyData& other) const {
    return surname < other.surname || (surname==other.surname && forename < other.forename); }
};

对于任何具有超过 2 个数据成员的事物来说,这变得非常难以管理。 有没有更简单的方法来实现它?数据成员可以是任何 Comparable 类。

c++ lexicographic
6个回答
18
投票

随着 C++11 的出现,有一种新的、简洁的方法可以使用 std::tie 来实现此目的:

bool operator<(const MyData& other) const {
  return std::tie(surname, forename) < std::tie(other.surname, other.forename);
}

12
投票

tuple
是一个好主意,但如果你想继续为你的成员变量命名,那么像这样重构你的比较函数可能就足够了:

struct MyData {
    string surname;
    string forename;
    string var;
    // ...

    bool operator<(const MyData& other) const {
        if (surname != other.surname) return surname < other.surname;
        if (forename != other.forename) return forename < other.forename;
        if (var != other.var) return var < other.var;

        // ...

        return false; //< They are equal
    }
};

根据您的口味,您甚至可能需要像

#define COMPARE(field) if (field != other.field) return field < other.field;
这样的宏来减少重复。然后该函数将变成一个
COMPARE
调用列表。


7
投票

您可以将数据存储在

boost::tuple
中,它提供字典比较,并提供命名访问器函数,类似于:

#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>

struct Data {
    string &surname()  {return stuff.get<0>();}
    string &forename() {return stuff.get<1>();}

    // it would be polite to add const overloads too.

    bool operator<(const Data &other) const {return stuff < other.stuff;}

private:
    boost::tuple<string, string> stuff;
};

我相信这也可以作为

std::tr1::tuple
提供,并且在即将推出的标准中将是
std::tuple

维护访问器列表可能比维护比较代码更易于管理。


3
投票

如果所有成员都有相同的类型,您可以将它们放入

std::vector
。默认情况下
std::lexicographical_compare
将用于比较向量。


2
投票

您可以使用具有内置词典比较功能的

boost::tuple
std::pair
。当然缺点是你不能将方法与元组关联起来。


0
投票

这里是与上面 Magnus Hoff 的答案相同的变体,但它使用参数数量可变的模板,并且适用于相同类型的任何对序列:

template<typename A, typename ... Args>
inline bool lt_lex(const A & a1, const A & a2, Args ... args)
{
    if constexpr (sizeof...(args) == 0)
    {
        return a1 < a2;
    }
    else
    {
        return (a1 == a2) ? cmp::lt_lex(args...) : (a1<a2);
    }
}

struct MyData{
    int a;
    char b;
    std::<string> c;
    bool operator<(const MyData& x) const 
    {
        return lt_lex(a, x.a,  b, x.b,  c, x.c);
    }
}

但是,请注意,上面的代码和建议的答案实际上进行了两次比较: 比如说

lt_lex(a1,a2, b1,b2) 

减少到

if(a1==a2)   // one comparison of a1 and a2
{
    return b1<b2;
}
else 
{
    return a1<a2;  // repeating comparison of a1 and a2
}

也许编译器可以对此进行优化,但这应该针对每种具体情况进行调查。

如果

  • 时间非常关键,例如当您需要频繁地对大量类实例进行排序时,
  • 班级有多名成员,
  • 所有成员都是整数,

可以通过计算和存储差异来改进代码

template<typename ... Args>
inline bool lt_lex(int & a1, int & a2, Args ... args)
{
    if constexpr (sizeof...(args) == 0)
    {
        return a1 < a2;
    }
    else
    {
        int d = a2-a1; // compute the difference once
        if (d==0) // check zero one bit
        {
            return cmp::lt_lex(args...);
        }
        else 
        {
            return d>0; // check sign bit
        };
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.