I have a struct MyStruct
which I have an unordered_set
for which has it's own hashing function MyStructHash
so that I can define unordered_set<MyStruct, MyStructHash> struct_set;
.
MyStruct
is something like:
struct MyStruct{
int a;
double c;
std::string b;
std::string toString()...
}
The hash function is:
struct MyStructHash{
std::size_t operator()(const MyStruct& ms) const{
return std::hash<std::string>()(ms.toString());
}
};
I then want to define an unordered_set<unordered_set<MyStruct>>
to stop there from being duplicates of the sets. I have treid a hash function like so:
struct MyStructHashSetHash{
size_t operator()(const std::unordered_set<MyStruct>& set) const{
size_t hash_val = 0;
for (const MyStructHash& obj: set){
hash_val ^= AbstractionHash{}(obj);
}
return hash_val;
}
};
I also have a similar equivalence function. Then I try to define my unordered_set as unordered_set<unordered_set<MyStruct, MyStructHash>, MyStructHashSetHash, MyStructSetEqual> struct_set_set;
However, when I do this I get the error:
/usr/include/c++/11/bits/hashtable_policy.h:1614:23: error: static assertion failed: key equality predicate must be invocable with two arguments of key type
1614 | static_assert(__is_invocable<const _Equal&, const _Key&, const _Key&>{},
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I can't find much about this error online. I see that there is an issue with the key, and that you must define a hash function for whatever it is you want an unordered_set for. But that is what I thought I had done with MyStructHashSetHash
.
Any help would be great. Thanks!
When I want to make a family of nested hashes, I use ADL.
namespace myns{
namespace adl{
template<class T>
std::size_t customhash(T const& t){
return ::std::hash<T>{}(t);
}
}
template<class T>
std::size_t autohash(T const&t){
using adl::customhash;
return customhash(t);
}
struct hasher {
template<class T>
std::size_t operator()(T const& t) const {
return autohash(t);
}
};
}
this is the basic framework.
You can use ::myns::hasher
as a hashing object in place of std::hash<T>
.
To extend it, you add customhash
overloads to either the namespace of your type, or myns::adl
. As you cannot add overloads to std
, to support hashing of unordered maps simply do:
namespace myns::adl{
template<class...Ts>
std::size_t customhash(std::unordered_map<Ts...> cobst& um ){
// has the unordered map
// to hash elements simply call customhash(element)
}
}
To support hashing your struct, do the same - overload customhash
.
And now recursive struct nested in unordered maps of unordered maps works.
(If you need to call customhash
from outside myns::adl
instead call myns::autohash
, or do the using trick.)