简体   繁体   中英

C++ unordered_map where key is also unordered_map

I am trying to use an unordered_map with another unordered_map as a key (custom hash function). I've also added a custom equal function, even though it's probably not needed.

The code does not do what I expect, but I can't make heads or tails of what's going on. For some reason, the equal function is not called when doing find(), which is what I'd expect.

unsigned long hashing_func(const unordered_map<char,int>& m) {
    string str;
    for (auto& e : m)
        str += e.first;
    return hash<string>()(str);
}
bool equal_func(const unordered_map<char,int>& m1, const unordered_map<char,int>& m2) {
    return m1 == m2;
}

int main() {

    unordered_map<
        unordered_map<char,int>, 
        string,
        function<unsigned long(const unordered_map<char,int>&)>,
        function<bool(const unordered_map<char,int>&, const unordered_map<char,int>&)>
        > mapResults(10, hashing_func, equal_func);

    unordered_map<char,int> t1 = getMap(str1);
    unordered_map<char,int> t2 = getMap(str2);

    cout<<(t1 == t2)<<endl; // returns TRUE
    mapResults[t1] = "asd";
    cout<<(mapResults.find(t2) != mapResults.end()); // returns FALSE

    return 0;
}

Comparing two std::unordered_map objects using == compares whether the maps contain the same keys. It does nothing to tell whether they contain them in the same order (it's an unordered map, after all). However, your hashing_func depends on the order of items in the map: hash<string>()("ab") is in general different from hash<string>()("ba") .

First of all, the equality operator is certainly required, so you should keep it.

Let's look at your unordered map's hash function:

string str;
for (auto& e : m)
    str += e.first;
return hash<string>()(str);

Since it's an unordered map, by definition, the iterator can iterate over the unordered map's keys in any order. However, since the hash function must produce the same hash value for the same key, this hash function will obviously fail in that regard.

Additionally, I would also expect that the hash function will also include the values of the unorderered map key, in addition to the keys themselves. I suppose that you might want to do it this way -- for two unordered maps to be considered to be the same key as long as their keys are the same, ignoring their values. It's not clear from the question what your expectation is, but you may want to think it over.

A good place to start is with what hashing_func returns for each map, or more easily what the string construction in hashing_func generates.

A more obviously correct hash function for such a type could be:

unsigned long hashing_func(const unordered_map<char,int>& m) {
    unsigned long res = 0;
    for (auto& e : m)
        res ^ hash<char>()(e.first) ^ hash<int>()(e.second);
    return res;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM