C ++ 11 хеш-функция для любого типа перечисления - PullRequest
10 голосов
/ 10 марта 2012

Я пишу хеш-функцию для моего объекта.Я уже могу хэшировать контейнеры и комбинировать хэши, благодаря функции Generic Hash для всех контейнеров STL Но у моих классов тоже есть перечисления.Конечно, я могу создать хеш-функцию для каждого перечисления, но это не очень хорошая идея.Можно ли создать какую-то общую спецификацию для std::hash, чтобы ее можно было применять к каждому перечислению?Нечто подобное, используя std::enable_if и 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.Этот код не компилируется

error: template parameters not used in partial specialization:
error:         ‘E’

Ответы [ 4 ]

11 голосов
/ 10 марта 2012

Ваш параметр E не может быть выведен, потому что компилятор не может знать, что ваш enable_if<...>::type в итоге снова обозначает E (и на самом деле, есть некоторые его специализации, которые по замыслу не делают этого!) , Это называется «невыбранный контекст» для E.

Если hash имеет только один параметр, я не знаю (как мне известно) о том, чтобы СФИНАИРОВАТЬ вашу частичную специализацию.

4 голосов
/ 12 июля 2013

Если вы хотите использовать макросы, вы можете вывести правильную специализацию std :: hash рядом с объявлением enum.

В противном случае единственный способ, которым я нашел возможность легко хэшировать значения перечисления, - этообобщить тип хэша:

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);
    }
};

и использовать его таким образом:

enum class E { a, b, c };
std::unordered_map<E, std:string, enum_hash> map;
map[E::a] = "a";
2 голосов
/ 26 апреля 2014

То, что вы пытаетесь сделать, запрещено стандартом.

[namespace.std]

Поведение программы на C ++ не определено, если она добавляет объявления или определения.в пространство имен std или в пространство имен в пространстве имен std, если не указано иное.

Программа может добавить специализацию шаблона для любого стандартного шаблона библиотеки в пространство имен std, только если объявление зависит от типа, определенного пользователем, и специализация соответствуетСтандартные требования к библиотеке для исходного шаблона явно не запрещены.

Так что вы, конечно, можете реализовать некоторые идеи в этих ответах, но вы не можете назвать это std :: hash.Определение собственного шаблона enum_hash кажется хорошей идеей.

2 голосов
/ 09 января 2014

Это возможно, перемещая ошибку специализации из списка аргументов шаблона, так что вывод параметра E все еще может происходить. Например, это может произойти в объявлении using:

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

(я обычно использую имя типа sfinae, чтобы я помню, почему он там есть.)

Демо-код:

#include <functional>

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

enum foo { BAR, BAZ };
class quux {};

int main() {
  // Compiles.
  std::hash<foo>foo_hash;
  // Gives a compile-time error: no type named ‘type’ in ‘struct
  // std::enable_if<false, quux>’.
  std::hash<quux>quux_hash;
  return 0;
}
...