如何将结构作为键插入到映射中

How can I insert a structure as key into a map?

本文关键字:插入 映射 结构      更新时间:2023-10-16

在从第一个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;
}

如何修复这个错误?

对于作为映射键的类型,它必须排序。实际上,这意味着必须为该类型定义operator<。如果您定义了一个全局operator<(const node&, const node&),这应该工作良好;例如,

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

std::map的键在内部存储在二叉搜索树中。为了在二叉搜索树中存储和搜索键,它们必须具有可比性。例如,二叉搜索树的一个要求是左子节点的键值小于其父节点的键值,而右子节点的键值大于其父节点的键值。但是,如果键不具有可比性,我们如何判断子键比父键大还是小呢?我们不能形成树,因此std::map不能处理这些类型。

你只需要像这样定义小于操作符:

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

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

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

下面是读取错误消息的方法:

/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<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = node]’,或者如果我们实际上做了替换,则调用‘bool std::less<node>::operator()(const node&, const node&) const’。这是一个问题,因为有no match for ‘operator<’ in ‘__x < __y’

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

它是如何做到的?当然是通过使用operator<。所以这就是我们需要做的来解决这个问题:它说operator<不存在被比较的东西,所以我们必须提供它。被比较的是什么?当然是node s。所以我们为我们的类定义了operator<

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

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

为什么int没有问题?您现在应该能够猜到:operator<已经自然地适用于int s。但是你必须告诉c++如何为任何用户类型做这件事,因为你可能有别的想法。

c++ 11

正如Andrew Rasmussen的回答所提到的,std::map的密钥必须具有可比性。但是,您也可以为您的映射提供一个自定义比较对象,而不是为您的结构定义operator<。此外,从c++ 11开始,您可以使用lambda表达式而不是定义比较对象。因此,您可以使代码保持如下所示的简短:

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

Ideone上的代码