Defining a hash function as part of a structure

I know that you can define a hash function for struct X by defining a separate struct hash function:

 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< that can be bound to struct X , for example. with set :

 struct X { bool operator<(const X &other) const {} }; int main() { set<X> s; } 

The ultimate 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 ++?

+6
source share
4 answers

std::unordered_set is defined in the std . And it uses std::hash structures for hash of many different types. If you want to use std::unordered_set<X> (without adding a lot of information to the declaration), you must create another overload of the std::hash template to make it a hash of your structure.

You should be able to make it work 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; } 

In addition, you must specify either overloading on std::equal_to , or the comparison operator==() ( operator==() ) for your structure. You must add one of the following values:

 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; } }; 
+5
source

There is no hash operator, but you can hide the hash structure inside your X :

 struct X { std::string name; struct hash { auto operator()( const X& x ) const { return std::hash< std::string >()( x.name ); } }; }; 

You can even make him a friend and make name private, etc.

Living example

+2
source
 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 diagram that installs an adl 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 need to specify a hash.

To add a new hash:

 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 containers and tuples std in the hashing namespace (override the hash function for them), and, oddly enough, they will work too.

+2
source

I would recommend considering a more general hash class. You can define for this class all the usual hash manipulation operations you may need:

 struct ghash { // all the values and operations you need here }; 

Then, in any class where you want to calculate the hash, you can define a conversion operator

 struct X { operator ghash () { // conversion operator ghash rh; // compute the hash return rh; } }; 

Then you can easily calculate the hash:

  X x; ghash hx = x; // convert x to a hash hx = (ghash)x; // or if you prefer to make it visible 

This will make it easier to expand the use of your hash structure without rethinking the general framework for any other X, Y, Z structure that may need a hash in the future.

Live demo here

+1
source

Source: https://habr.com/ru/post/986910/


All Articles