【问题标题】:How can I insert a structure as key into a map?如何将结构作为键插入到地图中?
【发布时间】:2011-10-30 01:46:08
【问题描述】:

在从第一行 insert 删除注释字符后,我收到以下代码的编译错误。我无法在插入整数时将结构插入到地图中。

# include <iostream>
# include <map>

using namespace std;

struct node
{int test;} temp;

int main()
{
    temp.test = 24;
    int test = 30;
    map<node, bool> mymap1;
    map<int, bool> mymap2;
    //mymap1.insert(make_pair(temp, true));
    mymap2.insert(make_pair(test, true));
    return 0;
}

我该如何解决这个错误?

【问题讨论】:

  • 注意:你也可以使用这个语法:mymap1[temp] = true; mymap2[test] = true;.

标签: c++ dictionary struct insert stdmap


【解决方案1】:

C++11

Andrew Rasmussen's answer 中所述,std::map 的键必须具有可比性。但是,您也可以为您的地图提供自定义比较对象,而不是为您的结构定义 operator&lt;。此外,由于C++11,您可以使用lambda expression 而不是定义比较对象。因此,您可以将代码保持如下:

auto comp = [](const node& n1, const node& n2) { return n1.test < n2.test; };
std::map<node, bool, decltype(comp)> mymap1(comp);

Code on Ideone

【讨论】:

    【解决方案2】:

    阅读错误信息的方法如下:

    /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = node]’:
    /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_tree.h:1141:   instantiated from ‘std::pair<typename std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(const _Val&) [with _Key = node, _Val = std::pair<const node, bool>, _KeyOfValue = std::_Select1st<std::pair<const node, bool> >, _Compare = std::less<node>, _Alloc = std::allocator<std::pair<const node, bool> >]’
    /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_map.h:469:   instantiated from ‘std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::insert(const std::pair<const _Key, _Tp>&) [with _Key = node, _Tp = bool, _Compare = std::less<node>, _Alloc = std::allocator<std::pair<const node, bool> >]’
    prog.cpp:15:   instantiated from here
    /usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_function.h:230: error: no match for ‘operator<’ in ‘__x < __y’
    

    首先,我们忽略了大部分“instantiated from”行,因为它们只是在谈论模板是如何扩展的。重要的是最后一个,指的是我们的源代码,因为它告诉我们错误是在哪里触发的。当然,无论如何我们都知道这一点,所以我们也将跳过它。我们还将忽略相关库头的路径,因为我们并不真正关心编译器如何存储其内容。

    stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = node]’:
    stl_function.h:230: error: no match for ‘operator<’ in ‘__x < __y’
    

    所以...我们的代码间接调用‘bool std::less&lt;_Tp&gt;::operator()(const _Tp&amp;, const _Tp&amp;) const [with _Tp = node]’,或者如果我们确实进行了替换,则‘bool std::less&lt;node&gt;::operator()(const node&amp;, const node&amp;) const’。这是一个问题,因为有no match for ‘operator&lt;’ in ‘__x &lt; __y’

    __x__ystd::less 实现中的变量(你应该可以猜到这么多)。从名称中,我们可以猜测(如果我们研究过标准库,我们就会知道)std::less 是一个模板函数,它比较两个相同类型的东西,并返回第一个是否小于第二个。

    它是如何做到的?当然,通过使用operator&lt;。所以这就是我们需要做的来解决这个问题:它说operator&lt; 不存在用于比较的内容,所以我们必须提供它。比较的是什么? nodes,当然。所以我们为我们的班级定义了operator&lt;

    为什么要这样做?这样我们就可以编写接受比较操作作为参数的函数(模板参数或运行时参数 - 但前者更常见),并传递std::less。这就是std::less存在的原因:它将比较事物的行为变成了一个函数,而实际的函数更有用一些。

    这有什么关系?因为,就像其他人所说的那样, std::map 实际上将 std::less 作为参数传递。它实际上是用于比较元素的std::map 模板的默认参数。毕竟,地图接口的一部分是每个键都是唯一的。如果您无法比较它们,您将如何检查键的唯一性?当然,从技术上讲,您只需要比较它们的平等性就可以了。但事实证明,能够对键进行排序使得创建更高效​​的数据结构成为可能。 (如果你真的在大学里学过编程和 CS 的课程,你就会知道这一点。)

    为什么int 没有问题?你现在应该可以猜到了:operator&lt; 已经自然地适用于ints。但是您必须告诉 C++ 如何为任何用户类型执行此操作,因为您可能有其他想法。

    【讨论】:

      【解决方案3】:

      std::map 的键内部存储在二叉搜索树中。为了在二叉搜索树中存储和搜索键,它们必须是可比较的。例如,二叉搜索树的要求是左孩子的键小于其父键,右孩子的键大于其父键。但是,如果键不可比较,我们应该如何判断孩子是否大于或小于父母?我们无法形成树,因此 std::map 不适用于这些类型。

      您只需要像这样定义小于运算符:

      bool operator<(const node& n1, const node& n2)
      {
          return n1.test < n2.test;
      }
      

      如果“test”数据成员是私有的,这也必须是你的节点结构的朋友(它现在是公共的,因为节点当前是一个结构)。但是,我可能会这样做:

      #include <map>
      
      class node
      {
          public:
              int getTest() const { return _test; }
              void setTest(int test) { _test = test; }
          private:
              int _test;
      };
      
      bool operator<(const node& n1, const node& n2)
      {
          return n1.getTest() < n2.getTest();
      }
      
      int main()
      {
          std::map<node,bool> foo;
          node n;
          n.setTest(25);
      
          foo[n] = true;
      
          return 0;
      }
      

      【讨论】:

      • 很好的解释。不要忘记structs 的成员默认是公开的。
      • getTest() 应该是 const 函数,否则你的 operator&lt; 将无法编译。
      • 是的,对不起,我直接把它写进了 SO。当我把它扔进我的编译器时,我才明白这一点:)
      【解决方案4】:

      对于用作映射键的类型,它必须是有序的。实际上,这意味着必须为该类型定义operator&lt;。如果你定义了一个全局operator&lt;(const node&amp;, const node&amp;),这应该可以正常工作;即,

      bool operator<(const node& n1, const node& n2) {
          return n1.test < n2.test;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-06-28
        • 2022-01-09
        • 2012-01-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多