不能使用枚举类作为unordered_map键

我有一个类包含一个枚举类。

class Shader { public: enum class Type { Vertex = GL_VERTEX_SHADER, Geometry = GL_GEOMETRY_SHADER, Fragment = GL_FRAGMENT_SHADER }; //... 

然后,当我在另一个类中实现下面的代码…

 std::unordered_map<Shader::Type, Shader> shaders; 

…我收到一个编译错误。

 ...usr/lib/c++/v1/type_traits:770:38: Implicit instantiation of undefined template 'std::__1::hash<Shader::Type>' 

这里是什么原因造成的?

我使用一个函数对象来计算enum class散列:

 struct EnumClassHash { template <typename T> std::size_t operator()(T t) const { return static_cast<std::size_t>(t); } }; 

现在你可以使用它作为std::unordered_map第三个模板参数:

 enum class MyEnum {}; std::unordered_map<MyEnum, int, EnumClassHash> myMap; 

所以你不需要提供std::hash的专门化,模板参数的推导就可以完成这个工作。 此外,您可以使用单词using并根据Keytypes使用std::hashEnumClassHash创build自己的unordered_map

 template <typename Key> using HashType = typename std::conditional<std::is_enum<Key>::value, EnumClassHash, std::hash<Key>>::type; template <typename Key, typename T> using MyUnorderedMap = std::unordered_map<Key, T, HashType<Key>>; 

现在,您可以使用带有enum class或其他types的MyUnorderedMap

 MyUnorderedMap<int, int> myMap2; MyUnorderedMap<MyEnum, int> myMap3; 

理论上, HashType可以使用std::underlying_type ,然后EnumClassHash将不是必需的。 这可能是这样的, 但我还没有尝试过

 template <typename Key> using HashType = typename std::conditional<std::is_enum<Key>::value, std::hash<std::underlying_type<Key>::type>, std::hash<Key>>::type; 

如果使用std::underlying_type作品,可能是一个非常好的标准提案。

这被认为是标准中的一个缺陷,并在C ++ 14中得到修复: http : //www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2148

从gcc 4.9.3开始,这个解决scheme还没有在libstdc ++中实现: https ://gcc.gnu.org/bugzilla/show_bug.cgi ? id = 60970。

它在2013年被clang的libc ++修复了: http : //lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20130902/087778.html

一个非常简单的解决scheme就是提供一个像这样的散列函数对象:

 std::unordered_map<Shader::Type, Shader, std::hash<int> > shaders; 

这就是一个枚举键,不需要提供std :: hash的专门化。

当你使用std::unordered_map ,你知道你需要一个哈希函数。 对于内置或STLtypes,有可用的默认值,但不适用于用户定义的默认值。 如果你只是需要一张地图,为什么不试试std::map

正如KerrekSB指出的那样,如果你想使用std::unordered_map ,你需要提供std::hash的专门化,如下所示:

 namespace std { template<> struct hash< ::Shader::Type > { typedef ::Shader::Type argument_type; typedef std::underlying_type< argument_type >::type underlying_type; typedef std::hash< underlying_type >::result_type result_type; result_type operator()( const argument_type& arg ) const { std::hash< underlying_type > hasher; return hasher( static_cast< underlying_type >( arg ) ); } }; } 

将此添加到定义MyEnumClass的标题中:

 namespace std { template <> struct hash<MyEnumClass> { size_t operator() (const MyEnumClass &t) const { return size_t(t); } }; } 

尝试

 std::unordered_map<Shader::Type, Shader, std::hash<std::underlying_type<Shader::Type>::type>> shaders;