任何枚举类型的C ++ 11哈希函数

时间:2012-03-10 12:08:38

标签: c++ templates enums c++11 hash-function

我正在为我的对象编写一个哈希函数。由于Generic Hash function for all STL-containers,我已经可以散列容器并组合散列。但我的课程也有枚举。当然我可以为每个枚举创建一个哈希函数,但这似乎不是一个好主意。是否可以为std::hash创建一些通用规范,以便它可以应用于每个枚举?类似的东西,使用std::enable_ifstd::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 个答案:

答案 0 :(得分:11)

无法推断出您的E参数,因为编译器无法知道您的enable_if<...>::type最终再次表示E(事实上,有一些专业化的设计要求那样做!)。它被称为E的“非推断上下文”。

如果hash只有一个参数,那么SFINAE就没有办法(我知道)你的部分专业化了。

答案 1 :(得分:4)

如果您愿意使用宏,则可以在枚举声明旁边转储正确的std :: hash特化。

否则,我发现轻松散列枚举值的唯一方法是概括散列类型:

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 :(得分:3)

通过将特化失败移出模板参数列表,可以实现E的参数推导。例如,您可以使用声明:

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

答案 3 :(得分:2)

标准禁止您尝试做的事情。

  

[namespace.std]

     

如果添加声明或者C ++程序的行为是未定义的   定义命名空间std或命名空间std中的命名空间   除非另有说明。

     

程序可以为任何程序添加模板专业化   标准库模板到命名空间std只有声明   取决于用户定义的类型和专业化符合   原始模板的标准库要求,而不是   明确禁止。

所以你当然可以在这些答案中追求一些想法,但你不能称之为std :: hash。定义自己的'enum_hash'模板似乎是一个好主意。