Каков самый простой способ определения лексикографического сравнения для элементов класса?

Если у меня есть класс, который я хочу, чтобы сортировать (т.е. поддерживать менее концепцию), и он имеет несколько элементов данных, так что мне нужно делать лексикографическое упорядочение, тогда мне нужно что-то вроде этого:

struct MyData {
  string surname;
  string forename;

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

Это становится довольно неуправляемым для чего-либо с более чем двумя членами данных. Есть ли более простые способы его достижения? Элементами данных могут быть любые классы Comparable.

Ответ 1

С появлением С++ 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 < other.field; для уменьшения дублирования. Тогда функция просто станет списком COMPARE -invocations.

Ответ 3

Вы можете сохранить данные в 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 в следующем стандарте.

Поддержание списка аксессуаров, вероятно, более управляемо, чем поддержание кода сравнения.

Ответ 4

Если все члены имеют один и тот же тип, вы можете поместить их в std::vector. По умолчанию std::lexicographical_compare будет использоваться для сравнения векторов.

Ответ 5

Вы можете использовать boost::tuple или std::pair, который имеет встроенное лексиграфическое сравнение. Конечно, недостатком является то, что вы не можете связать метод с кортежами.