Can't use enum class as unordered_map key

后端 未结 7 1329
后悔当初
后悔当初 2020-12-12 20:29

I have a class containing an enum class.

class Shader {
public:
    enum class Type {
        Vertex   = GL_VERTEX_SHADER,
        Geometry = GL_GEOMETRY_SHA         


        
相关标签:
7条回答
  • 2020-12-12 20:38

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

    0 讨论(0)
  • 2020-12-12 20:44

    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.

    0 讨论(0)
  • 2020-12-12 20:46

    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

    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.

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

    0 讨论(0)
  • 2020-12-12 20:53

    Add this to header defining MyEnumClass:

    namespace std {
      template <> struct hash<MyEnumClass> {
        size_t operator() (const MyEnumClass &t) const { return size_t(t); }
      };
    }
    
    0 讨论(0)
  • 2020-12-12 20:56

    Try

    std::unordered_map<Shader::Type, Shader, std::hash<std::underlying_type<Shader::Type>::type>> shaders;
    
    0 讨论(0)
  • 2020-12-12 21:02

    I use a functor object to calculate hash of 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:

    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 using and make your own unordered_map that use std::hash or EnumClassHash depending on the Key type:

    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 MyUnorderedMap with enum class or another type:

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

    Theoretically, HashType could use std::underlying_type and then the EnumClassHash will not be necessary. That could be something like this, but I haven't tried yet:

    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_type works, could be a very good proposal for the standard.

    0 讨论(0)
提交回复
热议问题