简体   繁体   中英

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?

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;

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.

When you are using a user defined class as key in std::map , in order to determine the position of the elements in the container the map needs the Comparison class: A class that takes two arguments of the key type and returns a bool.

It is basically, a comparison functor/ function which compares two key values.

I think the code above gives a little upgrade to @parapura rajkumar's solutions.

class Point{

    private:
        int x, y;

    public:
        bool operator<( const Point& other) const{
            return ((x < other.x) || (y < other.y));
        }
};

What you are saying as third parameter is called "Comparator" in STL. For default types as keys youy don't need to provide one as compiler does that job for you. But for your-defined types you have to provide it OR else how would compiler maintain the sort order in map/set etc.

You don't need third parameter, you just need the operator== and operator<

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM