Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defining hash function as part of a struct

Tags:

c++

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

like image 783
icedtrees Avatar asked May 10 '15 09:05

icedtrees


3 Answers

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

like image 58
Daniel Frey Avatar answered Oct 09 '22 15:10

Daniel Frey


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;
    }
};
like image 35
Rubens Avatar answered Oct 09 '22 15:10

Rubens


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.

like image 33
Yakk - Adam Nevraumont Avatar answered Oct 09 '22 16:10

Yakk - Adam Nevraumont