Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ STL map container with class key and class value

So suppose I have a class like this one:

class Point
{
   private:
      int x, y;
   public:
      void setX(int arg_x) { x = arg_x; }
      void sety(int arg_y) { y = arg_y; }
      int getX() const { return x; }
      int gety() const { return y; }
};

Now I want to have a map like this one:

map<Point, Point> m;

But I need a third parameter. I read in cplusplus that this third parameter is to compare something, but I didn't understand what that something was. Can anyone explain that for me?

like image 788
petermlm Avatar asked Aug 07 '11 14:08

petermlm


2 Answers

You can extend your class with such a method if you don't need a separate compare function

class Point
{
   private:
      int x, y;
   public:

      bool operator<( const Point& other) const
      {
          if ( x == other.x )
          {
              return y < other.y;
          }

          return x < other.x;
      }
};

By default the stl map orders all elements in it by some notion of ordering. In this case this operator is used. Sometimes you dont have control over the Point class or you might want to use it in two different maps each defines its own ordering. For example one map might sort points by x first and other one might sort by y first. So it might be helpful if the comparison operator is independent of the class Point. You can do something like this.

class Point
{
   public:
      int x, y;
};


struct PointComparer
{
    bool operator()( const Point& first , const Point& second) const
    {
        if ( first.x == second.x )
        {
            return first.y < second.y;
        }

        return first.x < second.x;
    }
};

map<Point, Point , PointComparer> m;
like image 157
parapura rajkumar Avatar answered Oct 13 '22 04:10

parapura rajkumar


What you need is to define an ordering of Point items.

This can be done in different ways :

Overload the operator < for Point

You can provide an overload of the < operator, whose prototype is :

bool operator < (const Point & p_lhs, const Point & p_rhs) ;

For example, for my tests, I used the following one :

bool operator < (const Point & p_lhs, const Point & p_rhs)
{
    if(p_lhs.getX() < p_rhs.getX()) { return true ; }
    if(p_lhs.getX() > p_rhs.getX()) { return false ; }
    return (p_lhs.getY() < p_rhs.getY()) ;
}

This is the easiest way, but it assumes, semantically, that the ordering defined above is the right default one.

Providing a functor

If you are unwilling to provide a < operator, or want to have multiple maps, each one with its own ordering, your solution is to provide a functor to the map. This is the third template parameter defined for the map:

template < class Key, class T, class Compare = less<Key>,
       class Allocator = allocator<pair<const Key,T> > > class map;

The functor must have the following signature :

struct MyCompareFunctor
{
    bool operator() (const Point & p_lhs, const Point & p_rhs)
    {
       // the code for comparison
    }
} ;

So, for my tests, I just wrote the following :

struct MyCompare
{
    bool operator() (const Point & p_lhs, const Point & p_rhs)
    {
        if(p_lhs.getX() > p_rhs.getX()) { return true ; }
        if(p_lhs.getX() < p_rhs.getX()) { return false ; }
        return (p_lhs.getY() > p_rhs.getY()) ;
    }
} ;

And used it in my map:

std::map<Point, Point, MyCompare> map ;

Et voilà...

Specializing std::less for Point

I see no point in doing this, but it's always good to know: You can specialize the std::less template structure for your Point class

#include <functional>

namespace std
{
    template<>
    struct less<Point> : binary_function <Point,Point,bool>
    {
        bool operator() (const Point & p_lhs, const Point & p_rhs)
        {
            if(p_lhs.getX() < p_rhs.getX()) { return true ; }
            if(p_lhs.getX() > p_rhs.getX()) { return false ; }
            return (p_lhs.getY() < p_rhs.getY()) ;
        }
    } ;
}

This has the same effect as overloading the operator <, at least, as far as the map is concerned.

As for the operator < solution above, semantically, this solution assumes that the ordering defined above is the right default one as far as std:less is concerned.

Note that the default std::less implementation calls the operator < of the is templated type. Having one giving different results than the other could be considered as a semantic error.

like image 40
paercebal Avatar answered Oct 13 '22 04:10

paercebal