I know that it's possible to define a hash function for a struct X
by defining a separate hash function struct:
struct hash_X {
size_t operator()(const X &x) const {}
bool operator()(const X &a, const X &b) const {}
};
int main() {
unordered_set<X, hash_X, hash_X> s;
}
But I'm looking for something like operator<
, which can be attached to struct X
itself, e.g. with set
:
struct X {
bool operator<(const X &other) const {}
};
int main() {
set<X> s;
}
The end goal is something like:
struct X {
size_t operator()(void) const {}
bool operator()(const X &other) const {}
};
int main() {
unordered_set<X> s;
}
Is this possible in C++?
There is no hash operator, but you could hide the hash
struct inside of your X
:
struct X
{
std::string name;
struct hash
{
auto operator()( const X& x ) const
{ return std::hash< std::string >()( x.name ); }
};
};
You could even make it a friend and make name
private, etc.
Live example
std::unordered_set
is defined within std
namespace. And it uses std::hash
structures to hash many different types. If you want to be able to use std::unordered_set<X>
(without adding much info to the declaration), you must create another overload of the std::hash
template, so as to make it hash your structure.
You should be able to get it working by doing the following:
# include <unordered_set>
struct X {
size_t operator()(void) const {}
bool operator()(const X &other) const {}
};
namespace std {
template<>
struct hash<X> {
inline size_t operator()(const X& x) const {
// size_t value = your hash computations over x
return value;
}
};
}
int main() {
std::unordered_set<X> s;
}
Andalso, you must provide either an overload to std::equal_to
, or a comparison operator (operator==()
) for your structure. You should add one of the following:
struct X {
...
inline bool operator==(const X& other) const {
// bool comparison = result of comparing 'this' to 'other'
return comparison;
}
};
Or:
template <>
struct equal_to<X> {
inline bool operator()(const X& a, const X& b) const {
// bool comparison = result of comparing 'a' to 'b'
return comparison;
}
};
namespace hashing {
template<class T>
std::size_t hash(T const&t)->
std::result_of_t<std::hash<T>(T const&)>
{
return std::hash<T>{}(t);
}
struch hasher {
template<class T>
std::size_t operator()(T const&t)const{
return hash(t);
}
};
}
the above is some boilerplate that sets up an adl-based hash
system.
template<class T>
using un_set=std::unordered_set<T,hashing::hasher>;
template<class K, class V>
using un_map=std::unordered_map<K,V,hashing::hasher>;
now creates two container aliases where you do not have to specify the hasher.
To add a new hashable:
struct Foo {
std::string s;
friend size_t hash(Foo const&f){
return hashing::hasher{}(s);
}
};
and Foo
works in un_set
and un_map
.
I would add support for std
containers and tuples into hashing
namespace (override the function hash
for them), and magically those will work too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With