Pour prendre en charge les types de clés définis par l'utilisateur dans std::unordered_set<Key>
et std::unordered_map<Key, Value>
il faut fournir operator==(Key, Key)
un foncteur de hachage:
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;
Il serait plus pratique d'écrire uniquement std::unordered_set<X>
avec un hachage par défaut pour le type X
, comme pour les types accompagnant le compilateur et la bibliothèque. Après consultation
- Projet de norme C ++ N3242 §20.8.12 [unord.hash] et §17.6.3.4 [hash.requirements],
- Boost.
- g ++
include\c++\4.7.0\bits\functional_hash.h
- VC10
include\xfunctional
- diverses questions connexes dans Stack Overflow
il semble possible de se spécialiser 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
}
Étant donné que le support du compilateur pour C ++ 11 est encore expérimental --- je n'ai pas essayé Clang ---, voici mes questions:
Est-il légal d'ajouter une telle spécialisation à l'espace de noms
std
? J'ai des sentiments mitigés sur ce sujet.Laquelle des
std::hash<X>::operator()
versions, le cas échéant, est conforme à la norme C ++ 11?Existe-t-il un moyen portable de le faire?
la source
operator==(const Key, const Key)
std::hash
(contrairement à d'autres choses dans l'std
espace de noms) est déconseillée par le guide de style de Google ; Prenez-le avec un grain de sel.Réponses:
Vous êtes expressément autorisé et encouragé à ajouter des spécialisations à l'espace de noms
std
*. La manière correcte (et essentiellement la seule) d'ajouter une fonction de hachage est la suivante:(D'autres spécialisations populaires que vous pourriez envisager de prendre en charge sont
std::less
,std::equal_to
etstd::swap
.)*) tant que l'un des types impliqués est défini par l'utilisateur, je suppose.
la source
unorder_map<eltype, hash, equality>
plutôt l' instanciation , pour éviter de gâcher la journée de quelqu'un avec des affaires ADL amusantes. ( Modifier conseils de Pete Becker sur ce sujet )operator==
.) Ma philosophie générale est que si la fonction est naturelle et essentiellement la seule "correcte" (comme la comparaison de paires lexicographiques), alors je l'ajoutestd
. Si c'est quelque chose de particulier (comme la comparaison de paires non ordonnées), je le rends spécifique à un type de conteneur.Mon pari serait sur l'argument du modèle Hash pour les classes unordered_map / unorder_set / ...:
Bien sûr
struct Xhasher { size_t operator(const X&) const; };
)std::hash<X>()
la source
std::hash
soit toujours la meilleure solution :-)char*
!hash
spécialisation interfère via ADL? Je veux dire, c'est tout à fait plausible, mais j'ai du mal à trouver un cas problématique.std::unordered_map<Whatever, Xunset>
et cela ne fonctionne pas car votreXunset
type de hachage n'est pas constructible par défaut.@Kerrek SB a couvert 1) et 3).
2) Même si g ++ et VC10 déclarent
std::hash<T>::operator()
avec des signatures différentes, les deux implémentations de bibliothèque sont conformes au Standard.La norme ne spécifie pas les membres de
std::hash<T>
. Il dit simplement que chacune de ces spécialisations doit satisfaire les mêmes exigences de "Hash" que celles requises pour le deuxième argument de modèle destd::unordered_set
et ainsi de suite. À savoir:H
est un objet fonction, avec au moins un type d'argumentKey
.H
est une copie constructible.H
est destructible.h
est une expression de typeH
ouconst H
, etk
est une expression d'un type convertible en (éventuellementconst
)Key
, alorsh(k)
est une expression valide avec typesize_t
.h
est une expression de typeH
ouconst H
, etu
est une lvaleur de typeKey
, alorsh(u)
est une expression valide avec un typesize_t
qui ne modifie pasu
.la source
std::hash<X>::operator()
plutôt questd::hash<X>
dans son ensemble, et la signature destd::hash<T>::operator()
est définie par l'implémentation.