2016-07-23 8 views
0

Ich bin kein Profi in C++, aber irgendwie habe ich eine Lösung bei der Portierung meines MSVS 2015 C++ - Code auf MinGW 4.9.2 zur Verfügung gestellt, um std::hash Klasse zu spezialisieren, um alle enum s zu unterstützen. Es gibt irgendwelche C++ - Compiler-Entwickler oder C++ - Pro-Programmierer hier, können Sie erklären, warum diese Spezialisierung funktioniert, obwohl es Undefined Behavior nach dem C++ - Standard heißt?Warum bietet dieser Code Spezialisierung für ** ALL ** enums für std :: hash template?

Link for full code with samples on Gist

#include <unordered_set> 
#include <functional> 

#if (defined __GNUC__) && (__GNUC__ < 6) 
// Adds support of std::hash<enum T> to libstdc++. 
// GCC 6 provides it out of the box. 
namespace std { 
    template<typename T> 
    struct hash { 
     constexpr size_t operator()(typename std::enable_if<std::is_enum<T>::value, T>::type s) const noexcept { 
      return static_cast<size_t>(s); 
     } 
    }; 
} 
#endif 

Unterstützung für die Bereitstellung von std::hash<enum T> bedeutet, dass alle Klassen wie std::unordered_XXX jede enum als Schlüssel unterstützen.

GCC 6.1.0 mit diese std::hash Definition mit Fehlern

error: redefinition of 'struct std::hash<_Tp>' 

GCC 5.3.0 ohne diese std::hash Definition nicht für std :: unordered_set mit folgendem Fehler fehl:

In file included from /usr/local/gcc-5.3.0/include/c++/5.3.0/bits/hashtable.h:35:0, 
       from /usr/local/gcc-5.3.0/include/c++/5.3.0/unordered_set:47, 
       from prog.cc:1: 
/usr/local/gcc-5.3.0/include/c++/5.3.0/bits/hashtable_policy.h: In instantiation of 'struct std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> >': 
/usr/local/gcc-5.3.0/include/c++/5.3.0/type_traits:137:12: required from 'struct std::__and_<std::__is_fast_hash<std::hash<Foo> >, std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> > >' 
/usr/local/gcc-5.3.0/include/c++/5.3.0/type_traits:148:38: required from 'struct std::__not_<std::__and_<std::__is_fast_hash<std::hash<Foo> >, std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> > > >' 
/usr/local/gcc-5.3.0/include/c++/5.3.0/bits/unordered_set.h:95:63: required from 'class std::unordered_set<Foo>' 
prog.cc:25:25: required from here 
/usr/local/gcc-5.3.0/include/c++/5.3.0/bits/hashtable_policy.h:85:34: error: no match for call to '(const std::hash<Foo>) (const Foo&)' 
    noexcept(declval<const _Hash&>()(declval<const _Key&>()))> 
           ^
In file included from /usr/local/gcc-5.3.0/include/c++/5.3.0/bits/move.h:57:0, 
       from /usr/local/gcc-5.3.0/include/c++/5.3.0/bits/stl_pair.h:59, 
       from /usr/local/gcc-5.3.0/include/c++/5.3.0/utility:70, 
       from /usr/local/gcc-5.3.0/include/c++/5.3.0/unordered_set:38, 
       from prog.cc:1: 
/usr/local/gcc-5.3.0/include/c++/5.3.0/type_traits: In instantiation of 'struct std::__not_<std::__and_<std::__is_fast_hash<std::hash<Foo> >, std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> > > >': 
/usr/local/gcc-5.3.0/include/c++/5.3.0/bits/unordered_set.h:95:63: required from 'class std::unordered_set<Foo>' 
prog.cc:25:25: required from here 
/usr/local/gcc-5.3.0/include/c++/5.3.0/type_traits:148:38: error: 'value' is not a member of 'std::__and_<std::__is_fast_hash<std::hash<Foo> >, std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> > >' 
    : public integral_constant<bool, !_Pp::value> 
... 

Antwort

4

Dies ist ein schlecht geformtes Programm, für das keine Diagnose erforderlich ist, da keine Basisspezialisierungen für Schablonentypen inangegeben werden dürfen.

Es funktionierte, weil in einigen Implementierungen, die solche eine Basis Spezialisierung allestd::hash<T> ohne eine explizite Spezialisierung auf Ihre Hash-Implementierung Umleitung geschieht. Dann funktioniert Ihr operator() nur noch mit enum s, aber das ist nicht, warum es funktioniert, noch verhindert es, dass es Ihr Programm schlecht geformt ohne Diagnose erforderlich macht.

Praktisch, es wahrscheinlich brach, weil jemand SFINAE std::hash mit einer leeren Basisimplementierung in gcc 6.1 aktiviert: dies kann durch einige C++ - Standard, unsicher, aber es ist eine Qualität der Implementierung Verbesserung, wenn nicht.

Der richtige Weg, dies zu tun ist

struct enum_hash { 
    template<typename T> 
    constexpr 
    typename std::enable_if<std::is_enum<T>::value,std::size_t>::type 
    operator()(T s) const noexcept { 
    return static_cast<std::size_t>(s); 
    } 
}; 

zu schaffen, die eine Art ist, die jede Enum Hash kann.

Jetzt übergeben ,enum_hash zu unordered_set<some_enum, enum_hash> so.

+0

Sieht so aus, als gäbe es nur Vorlage struct hash; in GCC <6.1.0. Ich meine, es gibt keinen Körper für diese Vorlage und das bedeutet, dass ich den Körper mit meinem Code versehe. Habe ich Recht? – slavanap

+0

@slav ja. Es ist immer noch ein schlecht geformtes Programm ohne Diagnose erforderlich, aber der Compiler erzeugt ein Programm ohne Fehlermeldung, und dieses Programm (dessen Verhalten nicht mehr durch den Standard geregelt wird) verhält sich aufgrund von, was Sie wollen Implementierungsquirken. Grundsätzlich lohnt es sich nicht, dies zu tun, wenn die Kosten, die eingespart werden, ein ", enum_hash" zu einer ungeordneten Map/Sets hinzufügen. – Yakk