使用std::tuple<int*,int*>为自定义键的unordered_map中vector内容丢失
我需要实现一个哈希映射,输入为两个3D坐标数组(int start[3]、int stop[3]),输出为std::vector<int>。为此我将std::tuple<int*, int*>定义为自定义类型CoordsKey,并重写operator(),基于坐标值创建std::tuple<int, int, int, int, int, int>,借助Boost计算哈希值。
测试时发现:在test()函数内,使用局部定义的坐标数组作为键,向map中添加vector并修改,此时vector大小为2;但返回main()函数后,用相同坐标值的局部数组作为键访问map,得到的vector内容丢失,大小为0。尽管两次访问的哈希值完全相同,但问题依然存在。而使用int作为键时,程序运行正常。
请问为何第一个程序失效,我的错误是什么?
核心错误在于你用指针作为哈希键的组成部分,但哈希映射的键相等判断逻辑是比较指针地址,而非指针指向的内容,具体原因如下:
键的相等判断逻辑不符合预期
你定义的CoordsKey是std::tuple<int*, int*>,对于std::unordered_map这类哈希容器,判断两个键是否相等时,默认使用std::tuple的operator==——它会直接比较两个指针的内存地址,而不是去对比指针指向的6个坐标值。test()函数里的局部数组和main()里的局部数组是完全独立的栈内存对象,地址必然不同,所以哪怕它们的坐标值完全一致,哈希容器也会把这两个CoordsKey判定为不同的键。哈希值相同不代表键相等
你看到两次哈希值相同只是发生了哈希碰撞,哈希容器在发现哈希值相同时,会进一步用operator==验证键是否真的相等。由于指针地址不同,验证不通过,容器会创建一个新的空std::vector<int>条目,而非返回你之前在test()中存储的那个。用
int作为键正常的原因
当键是int时,容器的相等判断是基于int的值比较,相同数值会被判定为同一个键,所以能正常访问到对应的存储内容。
修复方案
方案一(推荐):直接用值类型作为键
放弃指针封装,直接用存储坐标值的类型作为键,比如std::tuple<int, int, int, int, int, int>,这样哈希计算和相等判断都会基于坐标值,完全符合你的需求。方案二:自定义相等比较器
如果必须保留CoordsKey的指针封装,需要给哈希容器指定自定义的相等比较器,让它对比指针指向的坐标值而非地址:struct CoordsEqual { bool operator()(const CoordsKey& a, const CoordsKey& b) const { // 比较两个指针指向的6个int值是否全部相等 return std::memcmp(std::get<0>(a), std::get<0>(b), 3*sizeof(int)) == 0 && std::memcmp(std::get<1>(a), std::get<1>(b), 3*sizeof(int)) == 0; } }; // 使用时指定比较器 std::unordered_map<CoordsKey, std::vector<int>, YourHashFunc, CoordsEqual> your_map;
内容的提问来源于stack exchange,提问作者比尔盖子

