【问题标题】:C++ STL map container with class key and class value具有类键和类值的 C++ STL 映射容器
【发布时间】:2011-10-21 20:26:18
【问题描述】:

所以假设我有这样一个类:

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 中读到这第三个参数是比较某些东西,但我不明白那是什么东西。谁能帮我解释一下?

【问题讨论】:

  • 第三个参数在哪里?又是为了什么?您想在地图中存储 2 个点和其他内容吗?
  • 如果您要保留 set/get 功能,请使用公共成员:struct Point { int x, y; }; 更好。

标签: c++ map containers


【解决方案1】:

您所说的第三个参数在 STL 中称为“比较器”。 对于默认类型作为键,您不需要提供一个作为编译器 为你做这项工作。 但是对于您定义的类型,您必须提供它,否则编译器将如何维护 map/set 等中的排序顺序。

【讨论】:

    【解决方案2】:

    我认为上面的代码对@parapura rajkumar 的解决方案进行了一些升级。

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

    【讨论】:

    • 如果 x > other.x 这似乎不起作用,因为在这种情况下它将返回比较 y 值的结果。正如@user2963164 指出的那样,他的编辑被其他人拒绝,即使它看起来是有效的。
    • @mc110,此答案基于原始帖子。稍后我将对其进行编辑,以便针对编辑后的 ​​awnser 版本进行更正。
    【解决方案3】:

    您需要定义点项目的顺序。

    这可以通过不同的方式完成:

    为 Point 重载 operator &lt;

    您可以提供&lt; 运算符的重载,其原型为:

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

    这是最简单的方法,但从语义上讲,它假定上面定义的顺序是正确的默认顺序

    提供函子

    如果您不愿意提供&lt; 运算符,或者想要拥有多个地图,每个地图都有自己的顺序,您的解决方案是为地图提供函子。这是为地图定义的第三个模板参数:

    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 ;
    

    等等……

    std::less 专门用于Point

    我认为这样做没有任何意义,但知道总是很好:您可以为您的 Point 类专门化 std::less 模板结构

    #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 &lt; 具有相同的效果。

    至于上面的operator &lt; 解决方案,从语义上讲,这个解决方案假定上面定义的顺序是正确的默认顺序,就std:less 而言。

    请注意,默认的std::less 实现调用的是模板类型的operator &lt;。一个给出的结果与另一个不同可能被视为语义错误。

    【讨论】:

      【解决方案4】:

      如果你不需要单独的比较函数,你可以用这样的方法扩展你的类

      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;
      

      【讨论】:

      • 谢谢!这很有帮助。
      【解决方案5】:

      你不需要第三个参数,你只需要operator==operator&lt;

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

      【讨论】:

      • 地图不需要 == 运算符
      • 订购容器不需要operator==
      • 谢谢!这也很有帮助!
      【解决方案6】:

      当您使用用户定义的类作为 std::map 中的键时,为了确定元素在容器中的位置,地图需要比较类:一个带有两个参数的类键类型并返回一个布尔值。

      它基本上是一个比较两个键值的比较函子/函数。

      【讨论】:

      • 谢谢!这对很有帮助。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-18
      • 2015-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多