【问题标题】:What's the simplest way of defining lexicographic comparison for elements of a class?为类元素定义字典比较的最简单方法是什么?
【发布时间】:2010-03-23 14:28:10
【问题描述】:

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

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


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      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 &lt; other.field; 这样的宏来减少重复。然后该函数将成为COMPARE-invocations 的列表。

      【讨论】:

      • 可能是#define LEX_LT(mem) if(mem != other.mem) return mem &lt; other.mem,然后将它们链接为LEX_LT(surname); LEX_LT(forename); LEX_LT(var); ...; return false;
      • 我喜欢它的布局,它比嵌套 if 更易于管理且不易出错。我不反对偶尔使用宏,这对于较大的结构可能很方便,但我认为基本形式已经足够清晰了。
      【解决方案3】:

      您可以将数据存储在boost::tuple 中,该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

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

      【讨论】:

      • 有趣的解决方案。尽管在调试器中查看类的内容变得更加困难,但丢失命名字段似乎是一种耻辱。
      • 您可以保留命名字段并将元组仅用于比较目的,例如return boost::tie(surname, forename) &lt; boost::tie(other.surname, other.forename);
      • 更好的是,将tie 包装在一个成员函数中。然后你只需要维护一个字典顺序列表。
      • 整洁 - 我之前没有遇到过 boost::tie()
      【解决方案4】:

      如果所有成员的类型相同,您可以将它们放入std::vector。默认情况下,std::lexicographical_compare 将用于比较向量。

      【讨论】:

        【解决方案5】:

        您可以使用boost::tuplestd::pair,它具有内置的词法比较功能。当然缺点是你不能将方法关联到元组。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-03-06
          • 2017-07-24
          • 2014-07-15
          • 2021-05-01
          • 2011-02-17
          • 2014-06-03
          相关资源
          最近更新 更多