前提

最近在工作中,遇到了要将unordered_map定义的变量使用自定义的结构体作为key,然后使用int 作为value,遇到了一些问题,下面做个简单的总结。

问题复现

先看下面这段代码:

#include <iostream>
#include <unordered_map>
#include <cstdint>
#include <string>

struct RegInfo{
    uint32_t addr;
    std::string name;
};

int main()
{
    std::unordered_map<RegInfo, int> reg_map;
    return 0;
}

我们这里自定义了一个结构体RegInfo,然后利用这个结构体声明了一个unordered_map的变量,你可以尝试编译这个程序,会直接报错。报错内容大概如下:
在这里插入图片描述

重要的是error后面的信息*use of deleted function ‘std::unordered_map<_Key,_Tp, _Hash,_Pred, _Alloc>…’*巴拉巴拉一大堆的信息。

解决方案

所以unordered_map这种数据类型是如何定义的。
unordered_map的定义
通过模板进行定义的,KeyT我们都明白,代表了KeyValue的类型。那么Hash是什么,在我们不指定它时,它有一个默认参数std::hash<Key>KeyEqual从字面理解好像是判断Key是否相等,也有一个默认参数std::equal_to<Key>

这里的hash到底是在干什么呢?
答:unordered_map在c++中底层主要是基于哈希表(Hash Table)实现的,通过哈希函数将Key进行映射到数组索引上,从而实现对Key-Value的快速查找、插入、删除。它的查询效率和增删效率都是O(1)
unordered_map会使用用户指定或者默认的Hash类型(如std::hash<Key>)计算Key的哈希值。 好吧,结论出来了,我们没有指定Hash这个变量,因此它使用默认值std::hash<RegInfo>,但是没有std::hash<RegInfo>这个哈希函数,所以报错了。

那我们自己添加一个哈希函数吧,关于如何写哈希函数,可以参考这个网页。修改代码如下:

#include <iostream>
#include <unordered_map>
#include <cstdint>
#include <string>

struct RegInfo{
    uint32_t addr;
    std::string name;
};

struct KeyHash{  /* 自定义哈希函数 */
    size_t operator()(const RegInfo& key) const noexcept
    {
        size_t h1 = std::hash<uint32_t>{}(key.addr);
        size_t h2 = std::hash<std::string>{}(key.name);
        return h1 ^ (h2 << 1);  /* key 对应的哈希值 */
    }
};

int main()
{
    std::unordered_map<RegInfo, int, KeyHash> reg_map;
    return 0;
}

编译运行,可以通过。那么所有事情就结束了嘛?我们给定义的reg_map变量添加两个元素看看。添加如下几行代码:

int main()
{
    std::unordered_map<RegInfo, int, KeyHash> reg_map;
    RegInfo reg1{1, "reg1"};
    RegInfo reg2{2, "reg2"};

    reg_map[reg1] = 1;
    reg_map[reg2] = 2;
    return 0;
}

不出意外地出了意外,又报错了。报错信息如下:
在这里插入图片描述
这次的报错内容看起来就简单了许多,意思就是对于const RegInfo 类型没有匹配的==运算符。那我们给它加上==运算符试试(运算符重载)。
如下代码所示:

struct RegInfo{
    uint32_t addr;
    std::string name;
    bool operator==(const RegInfo& cmp) const
    {
        return (addr == cmp.addr) && (name == cmp.name);
    }
};

我们重载了==运算符,来判断两个RegInfo类型的变量是否相等。编译,运行,没有报错。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐