4

How to generalize the definition of < if the struct has arbitrarily many data members (< is to be defined using the order in which the data members are listed)? A simple example with 3 data members:

struct nData {
    int a;
    double b;
    CustomClass c;   // with == and < defined for CustomClass
    bool operator == (const nData& other) {return (a == other.a) && (b == other.b) && (c == other.c);}
    bool operator < (const nData& other) {
        if (  (a < other.a)  ||  ((a == other.a) && (b < other.b))  ||
                ((a == other.a) && (b == other.b) && (c < other.c))  )
            return true;
        return false;
    }
};

Using variadic templates and recursion somehow?

prestokeys
  • 4,817
  • 3
  • 20
  • 43

2 Answers2

15

You can use std::tie to create a tuple of references to the class members, and use the lexicographical comparison operators defined for tuples:

bool operator < (const nData& other) const {  // better make it const
    return std::tie(a,b,c) < std::tie(other.a, other.b, other.c);
}
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • 1
    And I suppose if you wanted descending sort for some variables, use `tie(a, other.b, c) < tie(other.a, b, other.c)` ? – Ben Voigt Feb 17 '14 at 16:58
  • 2
    One might refer to http://stackoverflow.com/questions/6218812/implementing-comparision-operators-via-tuple-and-tie-a-good-idea – user2672165 Feb 17 '14 at 16:58
  • Does it make sense to add an implicit conversion operator to a tuple of references, such that all these operators are implemented indirectly through tuple? Or is there a potential risk in doing so? (The same discussion for allowing int-wrappers to convert to int, such that we can calculate with those wrappers, lead to the conclusion that you should not do it.) – leemes Feb 17 '14 at 16:59
  • @Ben: Or `tie(a, -b, c) < tie(other.a, -other.b, other.c)` for types where `-` is cheap or readability is more important than performance I guess :) – Niklas B. Feb 17 '14 at 17:00
  • 1
    This is definitely the way to go if you can count on having it available. It _is_ a C++11 feature, however, so if you're stuck with an older compiler (like most people are)... – James Kanze Feb 17 '14 at 17:00
  • @BenVoigt: You could; though I'd call it something else if it didn't represent a "less than" relationship. – Mike Seymour Feb 17 '14 at 17:00
  • @James Kanze If you don't have C++11 you can still easily use `boost::tie` instead. – Mark B Feb 17 '14 at 17:44
  • @MarkB Can you? Without pulling in a lot of other things you don't want? (I don't know the situation with `boost::tie`, but I know that this is frequently a problem with Boost.) – James Kanze Feb 17 '14 at 19:24
3

This structure scales easily, and allows using arbitrary comparison functions (e.g. strcmp)

if (a != other.a) return a < other.a;
if (b != other.b) return b < other.b;
if (c != other.c) return c < other.c;
return false;
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • The above solutions are nice for up to, say 20, data members. Thank you very much. But what if there are hundreds of data members? e.g. int a1, a2, a3, ..., a1000; Is there a recursion method to handle that? – prestokeys Feb 17 '14 at 17:11
  • Edit, if the data members were int a1, a2, a3, ..., a1000; then std::vector would be the data member to hold all that instead (and then use std::lexicographical_compare). So what if the data members were int a1, a2, ..., a500; double b1, b2, ...., b300; etc... ? – prestokeys Feb 17 '14 at 17:42
  • @prestokeys: Your design is broken. If you have that many member variables, organize them in some sort of container (array, map, whatever). – Ben Voigt Feb 17 '14 at 19:15