Ich habe eine Klasse, die eine Aufzählungsklasse enthält.
class Shader {
public:
enum class Type {
Vertex = GL_VERTEX_SHADER,
Geometry = GL_GEOMETRY_SHADER,
Fragment = GL_FRAGMENT_SHADER
};
//...
Wenn ich dann den folgenden Code in einer anderen Klasse implementiere ...
std::unordered_map<Shader::Type, Shader> shaders;
... Ich erhalte einen Kompilierungsfehler.
...usr/lib/c++/v1/type_traits:770:38:
Implicit instantiation of undefined template 'std::__1::hash<Shader::Type>'
Was verursacht hier den Fehler?
std::hash
den Aufzählungstyp spezialisiert .Antworten:
Ich benutze ein Funktorobjekt, um den Hash von zu berechnen
enum class
:struct EnumClassHash { template <typename T> std::size_t operator()(T t) const { return static_cast<std::size_t>(t); } };
Jetzt können Sie es als 3. Template-Parameter von verwenden
std::unordered_map
:enum class MyEnum {}; std::unordered_map<MyEnum, int, EnumClassHash> myMap;
Sie müssen also keine Spezialisierung
std::hash
angeben. Der Abzug der Vorlagenargumente erledigt den Job. Darüber hinaus können Sie das Wort verwendenusing
und Ihr eigenes erstellenunordered_map
, dasstd::hash
oderEnumClassHash
je nachKey
Typ Folgendes verwendet: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>>;
Jetzt können Sie
MyUnorderedMap
mitenum class
oder einem anderen Typ verwenden:MyUnorderedMap<int, int> myMap2; MyUnorderedMap<MyEnum, int> myMap3;
Theoretisch
HashType
könnte man das gebrauchenstd::underlying_type
und dannEnumClassHash
wird das nicht nötig sein. Das könnte so etwas sein, aber ich habe es noch nicht versucht :template <typename Key> using HashType = typename std::conditional<std::is_enum<Key>::value, std::hash<std::underlying_type<Key>::type>, std::hash<Key>>::type;
Wenn
std::underlying_type
Werke verwendet werden, könnte dies ein sehr guter Vorschlag für den Standard sein.quelle
underlying_type
wird nicht funktionieren. Sie haben sich gezeigt: Es muss einehash()
Funktion geben, die einenMyEnumClass
Parameter akzeptiert. Also, natürlich,hash
ing aufunderlying_type
ruft eine Funktion , die eine erwartetint
(oder: yourEnumClassType
). Ich versuche nur,underlying_type
habe gezeigt , dass es genau die gleichen Fehler gibt: nicht umwandeln kannMyEnumClass
zuint
. Wenn nur beiläufigunderlying_type
tut Arbeit, so würde vorbeiMyEnumClass
direkt in dem ersten Platz. Wie David S zeigt, wurde dies nun von der Arbeitsgruppe behoben. Wenn nur GCC jemals ihren Patch veröffentlichen würde ...Dies wurde als Fehler im Standard angesehen und in C ++ 14 behoben: http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2148
Dies ist in der Version von libstdc ++ behoben, die mit gcc ab 6.1 ausgeliefert wird: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 .
Es wurde 2013 in clangs libc ++ behoben: http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20130902/087778.html
quelle
enum class
, als Schlüssel fürstd::unordered_set
Kompilierungen für Visual Studio 2013 verwendet zu werden.Eine sehr einfache Lösung wäre, ein Hash-Funktionsobjekt wie das folgende bereitzustellen:
std::unordered_map<Shader::Type, Shader, std::hash<int> > shaders;
Das ist alles für einen Enum-Schlüssel, ohne dass eine Spezialisierung auf std :: hash erforderlich ist.
quelle
Fügen Sie dies dem Header hinzu, der MyEnumClass definiert:
namespace std { template <> struct hash<MyEnumClass> { size_t operator() (const MyEnumClass &t) const { return size_t(t); } }; }
quelle
const noexcept
zur Signatur hinzufügen ?std
ist leider undefiniertes Verhalten.Wie KerrekSB hervorhob, müssen Sie eine Spezialisierung angeben,
std::hash
wenn Sie Folgendes verwenden möchtenstd::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 ) ); } }; }
quelle
Wenn Sie verwenden
std::unordered_map
, wissen Sie, dass Sie eine Hash-Funktion benötigen. Für integrierteSTL
Typen oder Typen sind Standardeinstellungen verfügbar, nicht jedoch für benutzerdefinierte. Wenn Sie nur eine Karte benötigen, warum versuchen Sie es nichtstd::map
?quelle
std::unordered_map
hat in fast allen Situationen eine überlegene Leistung und sollte wahrscheinlich eher als Standard betrachtet werden alsstd::map
.Versuchen
std::unordered_map<Shader::Type, Shader, std::hash<std::underlying_type<Shader::Type>::type>> shaders;
quelle
std::hash()
erwartet eine Instanzunderlying_type
als Parameter, wird aberMyEnumClass
stattdessen erhalten. Dies ist genau das Gleiche, wenn Sie versuchen, die alte einfacheenum
Spezifikationslösung zu verwendenstd::hash<int>
. Haben Sie dies versucht, bevor Sie es vorgeschlagen haben?enum class
betrifft nicht C-artigeenum
s. Sie werden sehen, dass mein Kommentar wahr istenum class
, was das Thema ist.