Нельзя использовать класс перечисления как 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>'
Что вызывает ошибку здесь?
Ответы
Ответ 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;