Подтвердить что ты не робот

Нельзя использовать класс перечисления как 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>'

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

4b9b3361

Ответ 1

Я использую объект-функтор для вычисления хэша 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 и создать свой собственный 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 а затем 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, это может быть очень хорошим предложением для стандарта.

Ответ 2

Это считалось дефектом в стандарте и было исправлено в С++ 14: http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2148

Это исправлено в версии libstdc++, поставляемой с gcc по состоянию на 6.1: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970.

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

Ответ 3

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

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

Что все для ключа перечисления, не нужно предоставлять специализацию std:: hash.

Ответ 4

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

Ответ 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 ) );
        }
    };
}

Ответ 6

Добавьте это в заголовок, определяющий MyEnumClass:

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

Ответ 7

Попробуйте

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