Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 Hash function for any enum type

I am writing a hash function for my object. I already can hash containers, and combine hashes, thanks to Generic Hash function for all STL-containers. But my classes also have enums. Of course I can create a hash function for every enum, but it does not seem like a good idea. Is it possible to create some generic specification for std::hash, so that it could be applied to every enum? Something like that, using std::enable_if and std::is_enum

namespace std {
  template <class E>
  class hash<typename std::enable_if<std::is_enum<E>::value, E>::type> {
  public:
    size_t operator()( const E& e ) const {
      return std::hash<std::underlying_type<E>::type>()( e );
    }
  };
};

PS. This code does not compile

error: template parameters not used in partial specialization:
error:         ‘E’
like image 331
Draco Ater Avatar asked Mar 10 '12 12:03

Draco Ater


3 Answers

Your E parameter cannot be deduced, because the compiler cannot know that your enable_if<...>::type ends up denoting E again (and in fact, there are some specializations of it that by design don't do that!). It's called a "non-deduced context" for E.

If hash has only one parameter, there is no way (that I am aware of) to SFINAE out your partial specialization.

like image 173
Johannes Schaub - litb Avatar answered Nov 07 '22 17:11

Johannes Schaub - litb


If you're willing to use macros, you could dump the correct std::hash specialization next to your enum declaration.

Otherwise, the only way I've found to easily hash enum values is to generalize the hash type:

struct enum_hash
{
    template <typename T>
    inline
    typename std::enable_if<std::is_enum<T>::value, std::size_t>::type
    operator ()(T const value) const
    {
        return static_cast<std::size_t>(value);
    }
};

and using it that way:

enum class E { a, b, c };
std::unordered_map<E, std:string, enum_hash> map;
map[E::a] = "a";
like image 33
Raphaël Londeix Avatar answered Nov 07 '22 16:11

Raphaël Londeix


What you're trying to do is prohibited by the standard.

[namespace.std]

The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified.

A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.

So you can certainly pursue some of the ideas in these answers, but you can't call it std::hash. Defining your own 'enum_hash' template seems like a good idea.

like image 2
Billy Donahue Avatar answered Nov 07 '22 16:11

Billy Donahue