不能将enum类用作unordered_map键

App*_*ell 71 c++ enums map c++11 enum-class

我有一个包含枚举类的类.

class Shader {
public:
    enum class Type {
        Vertex   = GL_VERTEX_SHADER,
        Geometry = GL_GEOMETRY_SHADER,
        Fragment = GL_FRAGMENT_SHADER
    };
    //...
Run Code Online (Sandbox Code Playgroud)

然后,当我在另一个类中实现以下代码时......

std::unordered_map<Shader::Type, Shader> shaders;
Run Code Online (Sandbox Code Playgroud)

...我收到编译错误.

...usr/lib/c++/v1/type_traits:770:38: 
Implicit instantiation of undefined template 'std::__1::hash<Shader::Type>'
Run Code Online (Sandbox Code Playgroud)

导致错误的原因是什么?

Dan*_*iel 110

我使用仿函数对象来计算哈希值enum class:

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

现在您可以将它用作第3个模板参数std::unordered_map:

enum class MyEnum {};

std::unordered_map<MyEnum, int, EnumClassHash> myMap;
Run Code Online (Sandbox Code Playgroud)

所以你不需要提供专业化std::hash,模板参数演绎就可以了.此外,您可以使用单词using并自行unordered_map使用std::hashEnumClassHash根据Key类型:

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>>;
Run Code Online (Sandbox Code Playgroud)

现在你可以使用MyUnorderedMap带有enum class或另一种类型:

MyUnorderedMap<int, int> myMap2;
MyUnorderedMap<MyEnum, int> myMap3;
Run Code Online (Sandbox Code Playgroud)

从理论上说,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;
Run Code Online (Sandbox Code Playgroud)

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


Dav*_*one 44

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

但是,从gcc 4.9.3开始,此解决方案尚未在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

  • @ypnos:这很可能是故意修复.它认为这在Visual Studio 2012中不起作用,因此它们可能在2013年将其修复为该缺陷的一部分.实际上,微软的C++标准库维护者STL是提供解决缺陷的措辞的人. (2认同)
  • @DavidStone:事实上,当前的Visual Studio不支持C++ 14甚至C++ 11.它们都包含C99,Visual Studio不支持.我不是说VS不应该默认使用最新的语言版本.我说当所有竞争编译器都支持某些标准时,它不应该引入它自己的事实上的标准.VS 2013在C++ 14最终确定之前出现了整整一年.然而,它不是完全支持C++ 11,而是包含C++ 14功能的一个子集. (2认同)
  • 相信我,我不只是给你一个链接 - 首先,页面*做*描述C++ 14,只是向上滚动.其次,"对垃圾收集的最小支持"*符合*标准.如果您阅读规范(链接那里!):"一个不支持垃圾收集的实现,并实现这里描述的所有库调用,因为no-ops符合要求." 这就是GCC的作用.而且我没有看到您编写代码的平台或者您认为哪种编译器很容易为讨论添加任何内容,这与**标准一致性**有关,而与单独感知的编译器质量无关. (2认同)

den*_*nim 21

一个非常简单的解决方案是提供这样的哈希函数对象:

std::unordered_map<Shader::Type, Shader, std::hash<int> > shaders;
Run Code Online (Sandbox Code Playgroud)

这就是枚举键的全部内容,不需要提供std :: hash的特化.

  • 这适用于老式的枚举,但不适用于OP正在使用的新热点"枚举类". (25认同)
  • 当它公然不回答这个问题时,它是如何达到+8的? (3认同)

use*_*602 6

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

namespace std {
  template <> struct hash<MyEnumClass> {
    size_t operator() (const MyEnumClass &t) const { return size_t(t); }
  };
}
Run Code Online (Sandbox Code Playgroud)

  • @VictorPolevoy :幸运的是,扩展 std: 是定义的行为,对于这种特殊情况。http://en.cppreference.com/w/cpp/language/extending_std (4认同)

CS *_*Pei 5

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

  • `std :: unordered_map`在几乎所有情况下都具有优越的性能,并且应该被认为是比`std :: map`更多的默认值. (24认同)

Dan*_*rey 5

正如KerrekSB指出的那样,std::hash如果你想使用它,你需要提供一个特殊化std::unordered_map,例如:

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 ) );
        }
    };
}
Run Code Online (Sandbox Code Playgroud)