Search code examples
c++boosthashcontainersunordered

Problems when using unordered_set as the key of unordered_map, in C++


The original problem is kind of long, so I simplify it here.

I need to create a group of strings, with a relevent integer, let's say a training group. Then I need to create many training groups. I want to manage all the training groups in a single container. So I decided to use boost::unordered_map<> with the key being std::unordered_set. Because, the BOOST has hash value for the standard C++ container.

The simplified code are as follows:

#include <string>
#include <unordered_set>
#include <utility>
#include<boost/unordered_map.hpp>

using namespace std;

int main()
{
    boost::unordered_map< unordered_set<string>, int> training_groups;
    pair<unordered_set<string>, int> a_training_group;
    training_groups.insert(a_training_group);
    return 0;
}

However, the code doesn't compile successfully. There are many cryptic warnings and an error. The error is as follows:

1>C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xhash(30): error C2440: 'type cast' : cannot convert from 'const std::unordered_set<std::string,std::hash<_Kty>,std::equal_to<_Kty>,std::allocator<_Kty>>' to 'size_t'
1>          with
1>          [
1>              _Kty=std::string
1>          ]
1>          No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>          C:\Program Files\boost\boost_1_59_0\boost/functional/hash/extensions.hpp(262) : see reference to function template instantiation 'size_t stdext::hash_value<T>(const _Kty &)' being compiled
1>          with
1>          [
1>              T=std::unordered_set<std::string,std::hash<std::string>,std::equal_to<std::string>,std::allocator<std::string>>
1>  ,            _Kty=std::unordered_set<std::string,std::hash<std::string>,std::equal_to<std::string>,std::allocator<std::string>>
1>          ]

I don't know where is the origin of this error and how to solve it. If the compiler cannot file the hash function of unordered_set, the error information will contains words like "Hash" or "Key". However, it just says something about type conversion, which looks similar to hash function. So, I feel confused.

Can anyone provide some advice. I am using Visual Studio 2013 on Windows 8.

PS: when I changed the Key unordered_set<string> to set<string> or vector<string>, the program compiles successfully. But I still don't know the reason, and don't know how to solve the problem if I am determined to use unordered_set<string> as the key.


Solution

  • Boost does not provide a hash function for std::unordered_set, the list of hash functions contains e.g. one for std::set:

    http://www.boost.org/doc/libs/1_61_0/doc/html/hash/reference.html#idp6283424-bb

    So you must provide your own hash function, which is relatively easy when using boost::hash_range:

    #include <string>
    #include <unordered_set>
    #include <utility>
    #include <boost/functional/hash/hash_fwd.hpp>
    
    namespace boost
    {
    template <class K, class C, class A>
    std::size_t hash_value(const std::unordered_set<K, C, A>& v)
    {
        return boost::hash_range(v.begin(), v.end());
    }
    } // namespace boost
    
    #include <boost/functional/hash.hpp>
    #include <boost/unordered_map.hpp>
    int main()
    {
        boost::unordered_map<std::unordered_set<std::string>, int> training_groups;
        std::pair<std::unordered_set<std::string>, int> a_training_group;
        training_groups.insert(a_training_group);
        return 0;
    }
    

    live example