C++11 Hash function for any enum type
Asked Answered
F

3

11

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’
Felker answered 10/3, 2012 at 12:8 Comment(0)
T
11

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.

Telegraph answered 10/3, 2012 at 12:14 Comment(0)
P
4

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";
Passerine answered 12/7, 2013 at 19:17 Comment(0)
S
2

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.

Sol answered 26/4, 2014 at 14:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.