Générique de hachage pour les tuples dans unordered_map / unordered_set
Pourquoi ne pas std::unordered_map<tuple<int, int>, string>
juste
travailler hors de la boîte?
Il est pénible d'avoir à définir une fonction de hachage pour tuple<int, int>
, par exemple
template<> struct do_hash<tuple<int, int>>
{ size_t operator()(std::tuple<int, int> const& tt) const {...} };
La construction d'une non-ordonnée carte avec les tuples que les clés (Matthieu M.) montre comment
automatiser ce pour boost::tuple
. Est-il de toute façon de le faire pour c++0x tuples sans l'aide de variadic templates?
Assurément, ce doit être dans la norme 🙁
- Si vous êtes à l'aide de C++0x pourquoi ne pas utiliser variadic templates? (ou est-il une mise en œuvre avec les tuples, mais pas de varidic modèles?)
- VC++ 2010 correspond à cette description (et il semble que la prochaine version de VC++ est probable aussi).
- Si std::tuple est mis en œuvre par l'écriture manuelle de modèles de 1 à N-arité (comme boost::tuple), alors je pense que le std::hash de la spécialisation nécessaire pour hacher les tuples devra également être écrit manuellement (en utilisant trop intelligent prétraitement?) de la même manière. Aaargh!
Vous devez vous connecter pour publier un commentaire.
Cela fonctionne sur gcc 4.5 permettant à tous de c++0x tuples contenant standard hashable types de membres de
unordered_map
etunordered_set
sans plus tarder.(J'ai mis le code dans un fichier d'en-tête et simplement l'inclure.)
La fonction est de vivre dans l'espace de noms std de sorte qu'il est pris par
argument dépendant de recherche de nom (ADL).
Est-il une solution plus simple?
Standard Conforme code
Yakk souligne que la spécialisation des choses dans l'espace de noms std est en fait un comportement indéterminé. Si vous souhaitez avoir des normes conformes solution, alors vous avez besoin de déplacer l'ensemble de ce code dans votre propre espace de noms et d'abandonner toute idée de l'ADL de trouver le bon hash mise en œuvre automatiquement. Au lieu de :
Vous avez besoin:
où
hash_tuple
est votre propre espace de noms plutôt questd::
.Pour ce faire, vous devez d'abord déclarer une valeur de hachage de la mise en œuvre à l'intérieur de la
hash_tuple
espace de noms. Cela permettra de transférer tous les non tuple types destd::hash
:Assurez-vous que
hash_combine
appelshash_tuple::hash
et passtd::hash
Puis d'inclure tous les autres code précédent, mais le mettre à l'intérieur
namespace hash_tuple
et passtd::
seed += hash(v); seed *= m
, où m est un grand nombre impair, est un bon choix, mais je ne sais pas si sa meilleure ou pire que la vôtre.std::
impliquant des choses que vous ne possédez pas, et que vous ne possédez passtd::tuple<TT...>
. Comme un exemple concret de la façon dont cela pourrait horriblement code de saut, ce qui arrive quand une nouvelle version de la norme introduit sa propre hachage de la spécialisation? Ce qui se passe quand quelqu'un d'autre qui a votre idée lumineuse présente une étroitehash<tuple<int>>
spécialisation, ce qui est visible à partir de certains mais pas de tous les lieux oùhash<tuple<int>>
est-il utilisé? Ceux sont des exemples concrets, mais l'UB n'est pas délimitée par eux. Votre programme est mal formé.std::
espace de noms.std
espace de noms uniquement pour les types personnalisés (donc pas pourstd::tuple
). C'est ce que Yakk mentionné dans son commentaire.std
espace de noms, le faire pour les types déjà dans la bibliothèque standard est lié à causer des problèmes.Dans mon C++0x projet,
20.8.15
dit de hachage est spécialisé pour les types intégrés (y compris les pointeurs, mais ne semble pas impliquer un déréférencement de mer). Il semble également être spécialisé pourerror_code
,bitset<N>
,unique_ptr<T, D>
,shared_ptr<T>
,typeindex
,string
,u16string
,u32string
,wstring
,vector<bool, Allocator>
, etthread::id
. (thème fascinant de la liste!)Je n'ai pas utilisé le C++0x variadics, donc ma mise en forme est probablement la façon de congé, mais quelque chose le long de ces lignes pourrait fonctionner pour tous les tuples.
Cette version compile et s'exécute
Yakk a observé que, spécialisée
std::hash
est directement techniquement pas autorisé, car nous sommes spécialisé une bibliothèque standard modèle avec une déclaration qui ne pas dépendent d'un type défini par l'utilisateur.left() ^ right()
si vous ne savez pas quoi faire. Voir stackoverflow.com/questions/5889238/... . Mais notez que XOR n'est pas toujours le bon choix. À l'aide de la plaine de plus, il peut à son tour d'être mieux si vous vous attendez à les tuples de contenir des doublons de membres. C'est probablement la raison pour laquelle il n'existe aucune norme de hachage pour les n-uplets.without using variadic templates?
oups. La réponse est non, ne peut pas être fait pour tous les tuples, depuis un tuple est un variadic type de modèle.+
sont à la fois commutative, et sont donc de mauvais choix pour combiner les valeurs de hachage. Examiner commentstd::unordered_set<std::tuple<int, int, …>>
serait en mesure de gérer les permutations de {1, 2, ..., 10}. Au lieu de cela, utilisez un non-commutative combiner commem * left + right
, où m est un grand nombre impair.hash
pour un type que vous n'avez pas de moyens propres à votre programme est mal formé. Et vous n'avez pas toushash<tuple<Ts...>>
. Et^
est horrible de hachage combiner.Avec C++20, il est possible d'utiliser plier les expressions et générique lambdas pour calculer le hachage d'un tuple sans récursivité. Je préfère me fier sur
std::hash<uintmax_t>
au lieu de saisir manuellement combinant les hachages:- 1
danssizeof(uintmax_t) * 4 - 1
est facultatif, mais semble légèrement s'améliorer de hachage de la distribution. Cette classe peut être utilisée à la fois avecstd::tuple
etstd::pair
.