J'ai une classe contenant une classe enum.
class Shader {
public:
enum class Type {
Vertex = GL_VERTEX_SHADER,
Geometry = GL_GEOMETRY_SHADER,
Fragment = GL_FRAGMENT_SHADER
};
//...
Ensuite, quand j'implémente le code suivant dans une autre classe ...
std::unordered_map<Shader::Type, Shader> shaders;
... Je reçois une erreur de compilation.
...usr/lib/c++/v1/type_traits:770:38:
Implicit instantiation of undefined template 'std::__1::hash<Shader::Type>'
Qu'est-ce qui cause l'erreur ici?
J'utilise un objet foncteur pour calculer le hash de enum class
:
struct EnumClassHash
{
template <typename T>
std::size_t operator()(T t) const
{
return static_cast<std::size_t>(t);
}
};
Vous pouvez maintenant l'utiliser comme 3ème template-paramètre de std::unordered_map
:
enum class MyEnum {};
std::unordered_map<MyEnum, int, EnumClassHash> myMap;
Donc, vous n'avez pas besoin de fournir une spécialisation de std::hash
, la déduction d’argument de gabarit fait le travail. De plus, vous pouvez utiliser le Word using
et créer votre propre unordered_map
qui utilise std::hash
ou EnumClassHash
selon le type 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>>;
Maintenant, vous pouvez utiliser MyUnorderedMap
avec enum class
ou un autre type:
MyUnorderedMap<int, int> myMap2;
MyUnorderedMap<MyEnum, int> myMap3;
Théoriquement, HashType
pourrait utiliser std::underlying_type
puis le EnumClassHash
ne sera pas nécessaire. Cela pourrait être quelque chose comme ça, mais je n'ai pas encore essayé:
template <typename Key>
using HashType = typename std::conditional<std::is_enum<Key>::value, std::hash<std::underlying_type<Key>::type>, std::hash<Key>>::type;
Si vous utilisez std::underlying_type
fonctionne, pourrait être une très bonne proposition pour la norme.
Ceci était considéré comme un défaut de la norme et a été corrigé en C++ 14: http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2148
À partir de la version 4.9.3 de gcc, cette résolution n’est pas encore implémentée dans libstdc ++: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=6097 .
Il a été corrigé dans la libc ++ de clang en 2013: http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20130902/087778.html
Une solution très simple serait de fournir un objet de fonction de hachage comme ceci:
std::unordered_map<Shader::Type, Shader, std::hash<int> > shaders;
C'est tout pour une clé enum, pas besoin de fournir une spécialisation de std :: hash.
Comme KerrekSB l'a souligné, vous devez fournir une spécialisation de std::hash
si vous voulez utiliser std::unordered_map
, quelque chose comme:
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 ) );
}
};
}
Quand vous utilisez std::unordered_map
, vous savez que vous avez besoin d’une fonction de hachage. Pour les types intégrés ou STL
, des valeurs par défaut sont disponibles, mais pas pour celles définies par l'utilisateur. Si vous avez juste besoin d’une carte, pourquoi ne pas essayer std::map
?
Ajoutez ceci à l'entête définissant MyEnumClass:
namespace std {
template <> struct hash<MyEnumClass> {
size_t operator() (const MyEnumClass &t) const { return size_t(t); }
};
}