C++ 不能使用枚举类作为 unordered_map 键

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18837857/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:14:54  来源:igfitidea点击:

Can't use enum class as unordered_map key

c++c++11mapenumsenum-class

提问by Appleshell

I have a class containing an enum class.

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

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

Then, when I implement the following code in another class...

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

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

...I get a compile error.

...我收到一个编译错误。

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

What is causing the error here?

是什么导致了这里的错误?

回答by Daniel

I use a functor object to calculate hash of enum class:

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

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

Now you can use it as 3rd template-parameter of std::unordered_map:

现在您可以将其用作 的第三个模板参数std::unordered_map

enum class MyEnum {};

std::unordered_map<MyEnum, int, EnumClassHash> myMap;

So you don't need to provide a specialization of std::hash, the template argument deduction does the job. Furthermore, you can use the word usingand make your own unordered_mapthat use std::hashor EnumClassHashdepending on the Keytype:

所以你不需要提供 的特化std::hash,模板参数推导就可以完成这项工作。此外,您可以使用这个词using并根据类型unordered_map使用std::hash或制作自己的词:EnumClassHashKey

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

Now you can use MyUnorderedMapwith enum classor another type:

现在您可以使用MyUnorderedMapwithenum class或其他类型:

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

Theoretically, HashTypecould use std::underlying_typeand then the EnumClassHashwill not be necessary. That could be something like this, but I haven't tried yet:

理论上,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;

If using std::underlying_typeworks, could be a very good proposal for the standard.

如果使用std::underlying_type作品,可能是标准的一个很好的建议。

回答by David Stone

This was considered a defect in the standard, and was fixed in C++14: http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2148

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

This is fixed in the version of libstdc++ shipping with gcc as of 6.1: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970.

从 6.1 开始,这在带有 gcc 的 libstdc++ 版本中已修复:https://gcc.gnu.org/bugzilla/show_bug.cgi?id =60970

It was fixed in clang's libc++ in 2013: http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20130902/087778.html

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

回答by denim

A very simple solution would be to provide a hash function object like this:

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

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

That's all for an enum key, no need to provide a specialization of std::hash.

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

回答by CS Pei

When you use std::unordered_map, you know you need a hash function. For built-in or STLtypes, there are defaults available, but not for user-defined ones. If you just need a map, why don't you try std::map?

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

回答by Daniel Frey

As KerrekSB pointed out, you need to provide a specialization of std::hashif you want to use std::unordered_map, something like:

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

回答by user3080602

Add this to header defining MyEnumClass:

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

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

回答by Vladimir Shutow

Try

尝试

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