C++ unordered_map与矢量一起使用时失败

C++ unordered_map fail when used with a vector as key

本文关键字:一起 失败 unordered map C++      更新时间:2023-10-16

>背景:我来自Java世界,我对C++或Qt相当陌生。

为了玩unordered_map,我编写了以下简单的程序:

#include <QtCore/QCoreApplication>
#include <QtCore>
#include <iostream>
#include <stdio.h>
#include <string>
#include <unordered_map>
using std::string;
using std::cout;
using std::endl;
typedef std::vector<float> floatVector;
int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    
    floatVector c(10);
    floatVector b(10);
    
    for (int i = 0; i < 10; i++) {
        c[i] = i + 1;
        b[i] = i * 2;
    }
    
    std::unordered_map<floatVector, int> map;
    
    map[b] = 135;
    map[c] = 40;
    map[c] = 32;
  
    std::cout << "b -> " << map[b] << std::endl;
    std::cout << "c -> " << map[c] << std::endl;
    std::cout << "Contains? -> " << map.size() << std::endl;
    
    return a.exec();
}

不幸的是,我遇到了以下错误,这并不鼓舞人心。甚至没有行号。

:-1:

错误:收集 2:ld 返回 1 个退出状态

知道问题的根源吗?

§

23.2.5,第3段说:

每个无序关联容器都由 Key 参数化,由满足 Hash 要求 (17.6.3.4( 的函数对象类型 Hash 参数化,并充当 Key 类型参数值的哈希函数,以及由在类型 Key 的值上诱导等价关系的二进制谓词Pred

使用 vector<float> 作为Key,不提供显式哈希和等效谓词类型意味着将使用默认std::hash<vector<float>>std::equal_to<vector<float>>

等价关系的std::equal_to很好,因为向量有一个运算符==,这就是std::equal_to使用的。

但是,没有std::hash<vector<float>>专业化,这可能是您没有向我们显示的链接器错误所说的。您需要提供自己的哈希器才能正常工作。

编写此类哈希器的一种简单方法是使用 boost::hash_range

template <typename Container> // we can make this generic for any container [1]
struct container_hash {
    std::size_t operator()(Container const& c) const {
        return boost::hash_range(c.begin(), c.end());
    }
};

然后,您可以使用:

std::unordered_map<floatVector, int, container_hash<floaVector>> map;

当然,如果你在映射中需要不同的相等语义,你需要适当地定义哈希和等价关系。


1. 但是,对于散列无序容器,请避免这样做,因为不同的顺序会产生不同的哈希值,并且无法保证无序容器中的顺序。

我发现 R. Martinho Fernandes 的答案不适合竞争性编程,因为大多数时候您必须处理提供的 IDE,并且无法使用外部库,例如 boost .如果您想充分利用 STL,可以使用以下方法。

如上所述,您只需要编写一个哈希函数。它应该专门用于存储在矢量中的数据类型。以下哈希函数假定int类型数据:

struct VectorHasher {
    int operator()(const vector<int> &V) const {
        int hash = V.size();
        for(auto &i : V) {
            hash ^= i + 0x9e3779b9 + (hash << 6) + (hash >> 2);
        }
        return hash;
    }
};

请注意,您可以使用任何类型的操作来生成哈希。您只需要发挥创造力,以便最大程度地减少碰撞。例如,hash^=V[i]hash|=V[i]hash+=V[i]*V[i]甚至hash+=(V[i]<<i)*(V[i]<<i)*(V[i]<<i)都是有效的,直到你的哈希不会溢出。

最后要将此哈希函数与您的unordered_map一起使用,请按如下方式初始化它:

unordered_map<vector<int>,string,VectorHasher> hashMap;