Не удается использовать класс enum в качестве неупорядоченного ключа карты


у меня есть класс, содержащий класс enum.

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

что вызывает ошибку здесь?

7 59

7 ответов:

Я использую объект функтора для вычисления хэша enum class:

struct EnumClassHash
{
    template <typename T>
    std::size_t operator()(T t) const
    {
        return static_cast<std::size_t>(t);
    }
};
вы можете использовать его в качестве 3-го шаблона-параметр std::unordered_map:
enum class MyEnum {};

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

так что вам не нужно предоставлять специализацию std::hash, вычитание аргумента шаблона выполняет эту работу. Кроме того, вы можете использовать слово using и сделать свой собственный unordered_map чтобы использовать std::hash или EnumClassHash в зависимости от 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>>;

теперь вы можете использовать MyUnorderedMap С enum class или другой тип:

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, однако, это разрешение еще не реализовано в libstdc++:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970.

Это было исправлено в libc++ clang в 2013 году: http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20130902/087778.html

очень простым решением было бы предоставить объект хэш-функции следующим образом:

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

это все для ключа перечисления, нет необходимости предоставлять специализацию std:: hash.

как отметил 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 ) );
        }
    };
}

при использовании std::unordered_map, вы знаете, вам нужна хэш-функция. Для встроенного или STL типы, есть доступные по умолчанию, но не для определенных пользователем. Если вам просто нужна карта, почему бы вам не попробовать std::map?

добавьте это в заголовок, определяющий 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;