Контейнер карты C ++ STL с ключом класса и значением класса - PullRequest
5 голосов
/ 07 августа 2011

Итак, предположим, у меня есть такой класс:

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

Теперь я хочу иметь такую ​​карту:

map<Point, Point> m;

Но мне нужен третий параметр. Я прочитал в cplusplus, что этот третий параметр - что-то сравнивать, но я не понял, что это было. Кто-нибудь может объяснить это для меня?

Ответы [ 6 ]

22 голосов
/ 07 августа 2011

Вы можете расширить свой класс таким методом, если вам не нужна отдельная функция сравнения

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

По умолчанию карта stl упорядочивает все элементы в ней по некоторому понятию упорядочения.В этом случае используется этот оператор.Иногда у вас нет контроля над классом Point или вы можете использовать его на двух разных картах, каждая из которых определяет свой собственный порядок.Например, одна карта может отсортировать точки по x сначала, а другая - по y сначала.Так что может быть полезно, если оператор сравнения не зависит от класса Point.Вы можете сделать что-то вроде этого.

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;
9 голосов
/ 07 августа 2011

Вам нужно определить порядок пунктов Point.

Это можно сделать разными способами:

Перегрузить operator < для Point

Вы можетеобеспечить перегрузку оператора <, прототип которого:

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

Например, для своих тестов я использовал следующий:

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

Это самый простой способ, но семантически предполагается, что определенный выше порядок является правильным по умолчанию .

Предоставление функтора

Если вы не желаете предоставлять оператор <или вы хотите иметь несколько карт, каждая из которых имеет свой собственный порядок, ваше решение заключается в предоставлении функтора для карты.Это третий параметр шаблона, определенный для карты:

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

Функтор должен иметь следующую подпись:

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

Итак, для моих тестов я просто написал следующее:

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

И использовал его на моей карте:

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

Et voilà ...

Специализируясь std::less для Point

Я не вижу смысла в этом, но всегда полезно знать: вы можете специализировать std::less структуру шаблона для вашего Point класса

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

Это имеет тот же эффект, что иперегрузка operator <, по крайней мере, что касается карты.

Что касается решения operator <, приведенного выше, семантически, это решение предполагает, что порядок, определенный выше, является правильным по умолчанию, посколькуstd:less.

Обратите внимание, что реализация по умолчанию std::less вызывает operator < типа шаблона.Наличие одного результата, отличного от другого, может рассматриваться как семантическая ошибка.

1 голос
/ 07 августа 2011

Когда вы используете пользовательский класс в качестве ключа в std :: map , для определения положения элементов в контейнере карта нуждается в классе сравнения:Класс, который принимает два аргумента типа ключа и возвращает bool.

По сути, это функция / функция сравнения, которая сравнивает два ключевых значения.

0 голосов
/ 15 сентября 2014

То, что вы говорите как третий параметр, называется "Компаратор" в STL. Для типов по умолчанию в качестве ключей вам не нужно указывать их как компилятор делает эту работу для вас. Но для ваших определенных типов вы должны предоставить его ИЛИ иначе, как будет поддерживать компилятор порядок сортировки в карте / наборе и т. д.

0 голосов
/ 25 марта 2014

Я думаю, что приведенный выше код немного обновляет решения @parapura rajkumar.

class Point{

    private:
        int x, y;

    public:
        bool operator<( const Point& other) const{
            return ((x < other.x) || (y < other.y));
        }
};
0 голосов
/ 07 августа 2011

Третий параметр вам не нужен, вам просто нужны operator== и 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;
}
...