簡體   English   中英

C++ unordered_map 在使用向量作為鍵時失敗

[英]C++ unordered_map fail when used with a vector as key

背景:我來自 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: 錯誤: collect2: ld 返回 1 個退出狀態

知道問題的根源嗎?

§23.2.5,第 3 段,說:

每個無序關聯容器由Key參數化,由滿足 Hash 要求 (17.6.3.4) 的函數對象類型Hash參數化並充當Key類型的參數值的散列函數,以及由二元謂詞Pred引起的等價關系Key類型的值。

使用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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM