I'm trying to use an unordered_set to maintain an unique list of structs. I've defined the hash function for the struct, Name, however I receive compile errors when I extend the Name struct to contain another struct member, Address. I know I need to specify how the Address struct must be hashed, but I can't seem to figure out where/how.
#include <unordered_set>
#include <string>
using namespace std;
struct Address
{
int num;
};
struct Name
{
string first;
string second;
Address address;
};
struct hashing_fn {
size_t operator()(const Address &a ) const
{
return hash<int>()(a.num);
}
size_t operator()(const Name &name ) const
{
return hash<string>()(name.first) ^ hash<string>()(name.second) ^ hash<Address>()(name.address);
}
};
int main(int argc, char* argv[])
{
unordered_set<Name,hashing_fn> ids;
return 0;
}
Just for completion, this was the fix:
template<>
struct hash<typename Address> {
size_t operator()(const Address &a ) const
{
return hash<int>()(a.num);
}
};
You never defined hash<Address>
! Instead, you have to use your own function operator()(name.address)
.
Simple XORing is maybe not the best solution. I strongly recommend you copy over Boost's hash_combine()
, and put the whole load into namespace std
:
template <class T>
inline void hash_combine(std::size_t & seed, const T & v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
namespace std
{
template <> struct hash<Address>
{
inline size_t operator()(const Address & a) const
{
return hash<int>()(a.num);
}
};
template <> struct hash<Name>
{
inline size_t operator()(const Name & a) const
{
size_t seed = 0;
hash_combine(seed, name.first);
hash_combine(seed, name.second);
hash_combine(seed, name.address);
return seed;
}
};
}
Now you can use the types directly (subject to implementing an equality comparator): std::unordered_set<Name> s;