Question

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.

Was it helpful?

Solution

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;
    }
}

OTHER TIPS

I assume you want to implement lexicographical ordering.

Prior to C++11:

#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);
}

Since C++11:

#include <tuple>
bool Class1::operator<(const Class1& other) const
{
    return std::tie(a, b, c) < std::tie(other.a, other.b, other.c);
}

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;

A version which avoids multiple indentation is

bool operator<(const Class1& other) const
{
    if(a != other.a)
    {
        return a < other.a;
    }

    if(b != other.b)
    {
        return b < other.b;
    }

    return c < other.c;
}

The "Edit 2" version of the author has on average more comparisons than this solution. (worst case 6 to worst case 3)

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top