views:

96

answers:

4

I have a class with a few numeric fields such as:

class Class1 {
    int a;
    int b;
    int c;
public:
    // constructor and so on...
    bool operator<(const Class1& other) const;
};

I need to use objects of this class as a key in an std::map. I therefore implement operator<. What is the simplest implementation of operator< to use here?

EDIT: The meaning of < can be assumed so as to guarantee uniqueness as long as any of the fields are unequal.

EDIT 2:

A simplistic implementation:

bool Class1::operator<(const Class1& other) const {
    if(a < other.a) return true;
    if(a > other.a) return false;

    if(b < other.b) return true;
    if(b > other.b) return false;

    if(c < other.c) return true;
    if(c > other.c) return false;

    return false;
}

The whole reason behind this post is just that I found the above implementation too verbose. There ought to be something simpler.

+11  A: 

I assume you want to implement lexicographical ordering.

#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
bool Class1::operator<(const Class1& other) const
{
    return boost::tie(a, b, c) < boost::tie(other.a, other.b, other.c);
}
avakar
Nice! But boost is too heavy for my particular case.
Vulcan Eager
Great, never thought of using tuples!
Matthieu M.
A: 

You could do:

return memcmp (this, &other, sizeof *this) < 0;

but that has quite a lot of of caveats - no vtbl for example and plenty more I'm sure.

Skizz
That will almost never work as intended.
Peter Alexander
@Peter: The OP only wants `to guarantee uniqueness as long as any of the fields are unequal`, so, adding an offsetof to get the address of the first key field and ensuring the key fields are contiguous, memcmp should do the trick.
Skizz
+3  A: 

It depends on if the ordering is important to you in any way. If not, you could just do this:

bool operator<(const Class1& other) const
{
    if(a == other.a)
    {
         if(b == other.b)
         {
             return c < other.c;
         }
         else
         {
             return b < other.b;
         }
    }
    else
    {
        return a < other.a;
    }
}
bshields
Thanks!! This is what I was looking for.
Vulcan Eager
Or, for fun, `return a!=other.a?a<other.a:b!=other.b?b<other.b:c<other.c;`
Skizz
+4  A: 

I think there is a misunderstanding on what map requires.

map does not require your class to have operator< defined. It requires a suitable comparison predicate to be passed, which conveniently defaults to std::less<Key> which uses operator< on the Key.

You should not implement operator< to fit your key in the map. You should implement it only if you to define it for this class: ie if it's meaningful.

You could perfectly define a predicate:

struct Compare: std::binary_function<Key,Key,bool>
{
  bool operator()(const Key& lhs, const Key& rhs) const { ... }
};

And then:

typedef std::map<Key,Value,Compare> my_map_t;
Matthieu M.