Search code examples
c++c++11unordered-set

Why can't I store my objects in an unordered_set?


I understand a set is ordered, thus adding an object without overloading the < operator doesn't allow to say which object is smaller to keep the container sorted. However, I don't understand why this isn't possible with an unordered_set.

If I try something like this:

#include <iostream>
#include <string
#include <unordered_set>

struct someType{
    string name;
    int code;
};

int main(){
    std::unordered_set <someType> myset;
    myset.insert({"aaa",123});
    myset.insert({"bbb",321});
    myset.insert({"ccc",213});
    return 0;
}

I get a couple of errors like:

c:\qt\qt5.1.0\tools\mingw48_32\lib\gcc\i686-w64-mingw32\4.8.0\include\c++\bits\hashtable_policy.h:1070: error: invalid use of incomplete type 'struct std::hash'

c:\qt\qt5.1.0\tools\mingw48_32\lib\gcc\i686-w64-mingw32\4.8.0\include\c++\bits\functional_hash.h:58: error: declaration of 'struct std::hash'

error: no matching function for call to 'std::unordered_set::unordered_set()'

c:\qt\qt5.1.0\tools\mingw48_32\lib\gcc\i686-w64-mingw32\4.8.0\include\c++\bits\hashtable_policy.h:1103: error: no match for call to '(const std::hash) (const someType&)'

c:\qt\qt5.1.0\tools\mingw48_32\lib\gcc\i686-w64-mingw32\4.8.0\include\c++\bits\stl_function.h:208: error: no match for 'operator==' (operand types are 'const someType' and 'const someType')

Why is that and how can I fix it?


Solution

  • To use type in unordered_set or unordered_map you need hashing function for your type. For common types, like int or std::string - hashing function is provided by standard library. For your type, you can overload standard std::hash, like this:

    namespace std {
        template <> struct hash<someType> {
            size_t operator()(const someType & x) const {
                std::hash<std::string> h;
                return h(x.name);
                // or simply return x.code
                // or do something more interesting,
                // like xor'ing hashes from both members of struct
            }
        };
    }
    

    Another way is to provide your own type with overloaded operator() and put it as hash template argument in unordered_set, like this:

    struct someTypeHasher {
        size_t operator()(const someType& x) const {
            return x.code;
        }
    };
    std::unordered_set<someType, someTypeHasher> myset;
    

    Good reading for theory about hash based containers is here

    Also, do not forget, that you need to overload operator== for someType, without it - it will also not work.