Tengo una clase que contiene una clase enum.

class Shader {
public:
    enum class Type {
        Vertex   = GL_VERTEX_SHADER,
        Geometry = GL_GEOMETRY_SHADER,
        Fragment = GL_FRAGMENT_SHADER
    };
    //...

Luego, cuando me implementar el siguiente código en la otra clase…

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

…Tengo un error de compilación.

...usr/lib/c++/v1/type_traits:770:38: 
Implicit instantiation of undefined template 'std::__1::hash<Shader::Type>'

Lo que está causando el error aquí?

  • No se especializan std::hash para el tipo enum.
InformationsquelleAutor Appleshell | 2013-09-16

7 Comentarios

  1. 106

    Yo uso un functor objeto de calcular el hash de enum class:

    struct EnumClassHash
    {
        template <typename T>
        std::size_t operator()(T t) const
        {
            return static_cast<std::size_t>(t);
        }
    };

    Ahora se puede utilizar como el 3 de plantilla-parámetro de std::unordered_map:

    enum class MyEnum {};
    
    std::unordered_map<MyEnum, int, EnumClassHash> myMap;

    Así que usted no necesita proporcionar una especialización de std::hash, el argumento de plantilla de la deducción que hace el trabajo. Además, usted puede utilizar la palabra using y hacer su propio unordered_map que utilizar std::hash o EnumClassHash dependiendo de la Key tipo:

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

    Ahora usted puede utilizar MyUnorderedMap con enum class o de otro tipo:

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

    Teóricamente, HashType podría utilizar std::underlying_type y, a continuación, el EnumClassHash no será necesario. Que podría ser algo como esto, pero no he probado aún:

    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 el uso de std::underlying_type funciona, podría ser una muy buena propuesta para el estándar.

    • Más simple tal vez, pero sólo llegar enum claves de trabajo en el primer lugar debe ser más sencillo? :-S
    • «En teoría», no, underlying_type no funcionará. Se mostró a sí mismo: debe ser un hash() función que toma un MyEnumClass parámetro. Así que, por supuesto, hashing en underlying_type invoca a una función que espera un int (o : yourEnumClassType). Tratando de underlying_type ha demostrado que da exactamente el mismo error: no se puede convertir MyEnumClass a int. Si de paso underlying_type ¿ de trabajo, para podría pasar MyEnumClass directamente en el primer lugar. De todos modos, como David S muestra, esto ha sido fijado ahora por el grupo de trabajo. Si sólo GCC nunca iba a liberar a sus parche…
    • Esto no funciona para mí en el caso de la clase enum era un protegido «miembro» de otra clase. Que necesitaba para pasar la enumeración de definición de la clase directamente dentro de una definición de espacio de nombres.
    • por algunas razones por las que estoy teniendo ningún problema en absoluto el uso de una clase enum como una clave en la desordenada mapa. yo uso sonar, tal vez apoyo depende del compilador? edit : como otra respuesta señala, esto es, en el estándar de c++14
  2. 40

    Esto fue considerado un defecto en la norma, y se fija en C++14: http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2148

    Como de gcc 4.9.3, sin embargo, esta resolución aún no está implementado en libstdc++: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970.

    Que se fija en el ruido de la libc++ en 2013: http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20130902/087778.html

    • Me sorprende enum class utiliza como clave en std::unordered_set compila para Visual Studio 2013.
    • No estoy sorprendido. Todos los tipos de no-estándar de mierda compila en Visual Studio, ha sido así desde hace siglos.
    • Lo más probable es intencional revisión. Se cree que esto no funciona en Visual Studio 2012, por lo que probablemente se fija en el 2013 como parte de ese defecto. De hecho, STL, el estándar de C++ biblioteca de mantenedor en Microsoft, es el que proporciona el texto para resolver el defecto.
    • No estoy diciendo que es mala, que fija o que su compilador no hace las cosas al azar. Sólo estoy diciendo que Microsoft siempre proporciona una especie de limbo estado de cumplimiento de las normas, donde algunas partes de la nueva norma son compatibles, otras no, y algunas partes de las próximas normas son compatibles temprano. Esto hace que sea mucho más difícil escribir en estándares de código que también se compila con otros toolchains. En GCC o ruido se puede especificar que la versión estándar de la programación de contra. Pero los programadores de Windows no decir «I programa de C++-11» o «I programa de C89». Ellos dicen «I programa de VS 2012″…
    • Yo prefiero el Visual Studio enfoque de simplemente poner a todos en la más reciente versión de la lengua, como C++ es C++14 ahora.
    • Como cuestión de hecho, la actual de Visual Studio no es compatible con C++14 o incluso C++11. Ambos son la C99, que no es compatible con Visual Studio. No estoy diciendo VS no debe predeterminado para la nueva versión de idioma. Estoy diciendo que no debe introducir su propio estándar de facto cuando hay ciertos estándares disponibles que son compatibles con todos los compiladores. VS 2013 salió un año antes de C++14 se llevó a cabo. Sin embargo, en lugar de apoyar completamente en C++11 incluye un subconjunto de C++14 características.
    • Vladimir la respuesta de abajo indica que VS2012 admite este (o alguna variación) en unordered_map, sin embargo, correcta o incorrecta podría hacerlo.
    • Sí, bueno, como se muestra por encima de gcc no implementar el estándar completo, por lo que es pot-hervidor de agua-negro.
    • ¿De qué estás hablando? GCC tiene pleno cumplimiento de todos los estándares de C++, vea gcc.gnu.org/projects/cxx-status.html#cxx11. Mientras que los desarrolladores de GCC también necesitan tiempo para implementar las nuevas normas, que luchan por el 100% de los estándares confirmance y que se tenga un registro de lograrlo. Es el opuesto de Microsoft, que deliberadamente no ir para el cumplimiento de estándares, pero, como dicen ellos, «características de nuestros clientes». También, mi idea principal era: En el GCC, Ruido, etc. Puedo elegir los estándares de la versión I programa contra, y obtener el comportamiento esperado con todo suficientemente recientes versiones del compilador.
    • La página no se describe en C++14 en todos y 1 C++11 característica es que falta («un Mínimo de apoyo para la recolección de basura y la accesibilidad basada en la detección de fugas de N2670 No»).
    • Y en realidad, como un desarrollador de C++ que escribe código para Linux y Windows, me parece Visual Studio C++ para ser muy bueno, cómodo utilizar el compilador. En algunas áreas es peor que GCC, en algunos es mejor.
    • Créeme, no me acaba de tirar un enlace a usted – en Primer lugar, la página de no describir C++14, simplemente desplácese hasta. Segundo, el «Mínimo de apoyo para la recolección de basura» es normas conformes. Si usted lee la especificación (vinculada allí!): «Una implementación que no admite la recolección de basura e implementa toda la biblioteca-las llamadas se describe aquí como el no-ops que se está conformando.» Esto es lo que GCC no. Y no veo cómo las plataformas de escribir código o compilador que se encuentre cómodo añade nada a la discusión, que era acerca de cumplimiento de estándares y no de forma individual percibida compilador de calidad.

  3. 20

    Una solución muy simple sería la de proporcionar una función hash objeto como este:

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

    Eso es todo por una clave de enumeración, no es necesario proporcionar una especialización de std::hash.

    • Esto funciona para la vieja timey las enumeraciones, pero no para la nueva caliente «enum clases», como el OP está utilizando.
    • ¿De qué manera llegar a +8 cuando manifiestamente no responde a la pregunta?
    • ^ Bueno, como por Vladimir respuesta a continuación, tal vez dril de algodón a prueba esta en VS2012 o algún otro compilador que de alguna manera permite esto.
  4. 5

    Como KerrekSB señaló, es necesario proporcionar una especialización de std::hash si desea utilizar std::unordered_map, algo así como:

    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 ) );
            }
        };
    }
  5. 5

    Cuando se utiliza std::unordered_map, usted sabe que usted necesita de una función hash. Para integrado o STL tipos, hay valores predeterminados disponibles, pero no para los definidos por el usuario. Si usted sólo necesita un mapa, ¿por qué no intentar std::map?

    • std::unordered_map tiene un rendimiento superior en casi todas las situaciones, y probablemente debería ser considerado más un defecto que std::map.
  6. 4

    Agregue esto a la cabecera de la definición de MyEnumClass:

    namespace std {
      template <> struct hash<MyEnumClass> {
        size_t operator() (const MyEnumClass &t) const { return size_t(t); }
      };
    }
    • No debe agregar const noexcept a la firma?
    • Ampliación de std es un comportamiento indefinido por desgracia.
    • ampliación sexual: se define el comportamiento afortunadamente, para este caso especial. en.cppreference.com/w/cpp/language/extending_std
    • oh, gracias por el comentario!
    • Creo que llvm 8.1 tiene un problema con esto, pero en otros compiladores funciona bien.
  7. -1

    Intentar

    std::unordered_map<Shader::Type, Shader, std::hash<std::underlying_type<Shader::Type>::type>> shaders;
    • No, no funcionará. Que std::hash() espera una instancia de underlying_type como un parámetro pero consigue MyEnumClass lugar. Esto es exactamente lo mismo que sucede cuando se intenta utilizar la antigua llanura enum solución de especificar std::hash<int>. Hizo probar esto antes de enviarlo?
    • seguro, yo lo hice. Compila bien en VS 2012. Exactamente este «espacio de nombres ObjectDefines { enum ObjectType { ObjectHigh, …. } } std::unordered_map<ObjectDefines::ObjectType, ObjectData* std::hash<std::underlying_type<ObjectDefines::ObjectType>::tipo de>> m_mapEntry;»
    • La pregunta es acerca de enum class, no al estilo C sin ámbito enums. Verás que mi comentario es cierto para enum class, que es el sujeto.
    • Veo la diferencia. Pero todavía compila bien: clase ObjectDefines { public: clase enum ObjectType { ObjectHigh, ObjectLow }; }; std::unordered_map<ObjectDefines::ObjectType, ObjectDefines* std::hash<std::underlying_type<ObjectDefines::ObjectType>::tipo de>> m_mapEntry;
    • Muy interesante! Basado en otras respuestas, especialmente la que se citando la Norma defecto, estoy bastante seguro de que este no de trabajo… (& no sé cómo lo he puesto en práctica.) Como mi anterior prueba, el mismo código de GCC (con #include <unordered_map> añadido) produce un clásico de la pila de la plantilla de los mensajes de error de que el primer & más útil es /usr/include/c++/5/bits/hashtable_policy.h:85:34: error: no match for call to ‘(const std::hash<int>) (const ObjectDefines::ObjectType&)’ . LLVM/clang++ está de acuerdo. ¿Alguien puede confirmar si VS2012 está bien o mal, & tal vez probar la última versión?
    • Puedo comprobar en VS 2013 funciona así.
    • en gcc 4.7.3 compila demasiado (comprueba aquí melpon.org/wandbox/permlink/k2FopvmxQeQczKtE)
    • Sí, pero no en 5.2.0 disponible en la misma página, o en la más reciente versión v4 4.9.2. Esto se pone más raro…

Dejar respuesta

Please enter your comment!
Please enter your name here