Benutzerdefinierte Schlüsseltypen in zu unterstützen std::unordered_set<Key>
und std::unordered_map<Key, Value>
man hat zu schaffen operator==(Key, Key)
und ein Hash Funktors:
struct X { int id; /* ... */ };
bool operator==(X a, X b) { return a.id == b.id; }
struct MyHash {
size_t operator()(const X& x) const { return std::hash<int>()(x.id); }
};
std::unordered_set<X, MyHash> s;
Es wäre bequemer, nur std::unordered_set<X>
mit einem Standard-Hash für Typ zu schreiben X
, wie für Typen, die mit dem Compiler und der Bibliothek geliefert werden. Nach Rücksprache mit
- C ++ Standard Draft N3242 §20.8.12 [unord.hash] und §17.6.3.4 [hash.requirements],
- Boost.Unordered
- g ++
include\c++\4.7.0\bits\functional_hash.h
- VC10
include\xfunctional
- verschiedene verwandte Fragen im Stapelüberlauf
es scheint möglich zu spezialisieren std::hash<X>::operator()
:
namespace std { // argh!
template <>
inline size_t
hash<X>::operator()(const X& x) const { return hash<int>()(x.id); } // works for MS VC10, but not for g++
// or
// hash<X>::operator()(X x) const { return hash<int>()(x.id); } // works for g++ 4.7, but not for VC10
}
Angesichts der Tatsache, dass die Compiler-Unterstützung für C ++ 11 noch experimentell ist - ich habe Clang nicht ausprobiert -, sind dies meine Fragen:
Ist es legal, dem Namespace eine solche Spezialisierung hinzuzufügen
std
? Ich habe gemischte Gefühle.Welche der
std::hash<X>::operator()
Versionen entspricht gegebenenfalls dem C ++ 11-Standard?Gibt es eine tragbare Möglichkeit, dies zu tun?
quelle
operator==(const Key, const Key)
std::hash
(im Gegensatz zu anderen Dingen imstd
Namespace) vom Google Style Guide nicht empfohlen wird. nimm es mit einem Körnchen Salz.Antworten:
Es wird ausdrücklich gestattet und empfohlen, dem Namespace * Spezialisierungen hinzuzufügen
std
. Der richtige (und im Grunde einzige) Weg, eine Hash-Funktion hinzuzufügen, ist folgender:(Andere beliebte Spezialisierungen, die Sie möglicherweise unterstützen möchten, sind
std::less
,std::equal_to
undstd::swap
.)*) Solange einer der beteiligten Typen benutzerdefiniert ist, nehme ich an.
quelle
unorder_map<eltype, hash, equality>
stattdessen die Instanziierung vorziehen , um nicht den Tag eines Menschen mit lustigen ADL-Geschäften zu ruinieren. ( Bearbeiten Sie Pete Beckers Rat zu diesem Thema )operator==
.) Meine allgemeine Philosophie lautet: Wenn die Funktion natürlich und im Wesentlichen die einzige "richtige" ist (wie der lexikografische Paarvergleich), füge ich sie hinzustd
. Wenn es etwas Besonderes ist (wie ein ungeordneter Paarvergleich), dann mache ich es spezifisch für einen Containertyp.Meine Wette wäre auf das Hash-Vorlagenargument für die Klassen unordered_map / unorder_set / ...:
Natürlich
struct Xhasher { size_t operator(const X&) const; };
)std::hash<X>()
quelle
std::hash
char*
!hash
Spezialisierung über ADL stört? Ich meine, es ist durchaus plausibel, aber es fällt mir schwer, einen Problemfall zu finden.std::unordered_map<Whatever, Xunset>
und es funktioniert nicht, weil IhrXunset
Hasher-Typ nicht standardmäßig konstruierbar ist.@ Kerrek SB hat 1) und 3) abgedeckt.
2) Obwohl g ++ und VC10
std::hash<T>::operator()
mit unterschiedlichen Signaturen deklarieren , sind beide Bibliotheksimplementierungen Standard-kompatibel.Der Standard spezifiziert nicht die Mitglieder von
std::hash<T>
. Es heißt nur, dass jede solche Spezialisierung die gleichen "Hash" -Anforderungen erfüllen muss, die für das zweite Vorlagenargument vonstd::unordered_set
und so weiter erforderlich sind . Nämlich:H
ist ein Funktionsobjekt mit mindestens einem ArgumenttypKey
.H
ist kopierkonstruierbar.H
ist zerstörbar.h
es sich um einen Ausdruck vom TypH
oderconst H
handelt undk
ein Ausdruck eines Typs ist, der in (möglicherweiseconst
) konvertierbar istKey
, dannh(k)
handelt es sich um einen gültigen Ausdruck mit Typsize_t
.h
es sich um einen Ausdruck vom TypH
oderconst H
handelt undu
ein Wert vom Typ istKey
,h(u)
handelt es sich um einen gültigen Ausdruck mit dem Typ,size_t
der nicht geändert wirdu
.quelle
std::hash<X>::operator()
eher alsstd::hash<X>
Ganzes zu spezialisieren , und die Signatur vonstd::hash<T>::operator()
ist implementierungsdefiniert.