参考:https://blog.csdn.net/pineappleKID/article/details/108341064
想完成的任务 与 遇到的问题
想在c++11 的unordered_set和unordered_map中插入pair或tuple作为键值
std::unordered_map<std::pair<std::string,std::string>, int> m;
会报错
/usr/include/c++/4.9/bits/hashtable_policy.h: In instantiation of ‘struct std::__detail::__is_noexcept_hash<std::tuple<int, int>, std::hash<std::tuple<int, int> > >’
或者
/usr/include/c++/4.9/bits/hashtable_policy.h: In instantiation of ‘struct std::__detail::__is_noexcept_hash<std::pair<std::basic_string, std::basic_string >, std::hash<std::pair<std::basic_string, std::basic_string > > >’
C++的std::pair是无法std::hash的,为了在unordered_set和unordered_map中使用std::pair,有如下方法。还有个前提,pair 和 tuple 中的元素本身得是可以 std::hash 哈希的。
方法一:专门写个可用于std::pair的std::hash
#include <iostream>
#include <unordered_map>
#include <utility>typedef std::pair<std::string,std::string> pair;struct pair_hash
{template <class T1, class T2>std::size_t operator() (const std::pair<T1, T2> &pair) const{return std::hash<T1>()(pair.first) ^ std::hash<T2>()(pair.second);}
};int main()
{std::unordered_map<pair,int,pair_hash> unordered_map ={{{"C++", "C++11"}, 2011},{{"C++", "C++14"}, 2014},{{"C++", "C++17"}, 2017},{{"Java", "Java 7"}, 2011},{{"Java", "Java 8"}, 2014},{{"Java", "Java 9"}, 2017}};for (auto const &entry: unordered_map){auto key_pair = entry.first;std::cout << "{" << key_pair.first << "," << key_pair.second << "}, "<< entry.second << '\n';}return 0;
}
输出
{Java,Java 8}, 2014
{Java,Java 7}, 2011
{Java,Java 9}, 2017
{C++,C++17}, 2017
{C++,C++14}, 2014
{C++,C++11}, 2011
注意:上面的代码使用的异或(XOR),由于x^x == 0并且x^y == y^x,所以应该配合一些位运算的shift或rotate来做。
方法二:使用boost::hash
boost::hash可以用于哈希integers, floats, pointers, strings, arrays, pairs 以及其它 STL 里的东西
#include <iostream>
#include <boost/functional/hash.hpp>
#include <unordered_map>
#include <utility>typedef std::pair<std::string,std::string> pair;int main()
{std::unordered_map<pair,int,boost::hash<pair>> unordered_map ={{{"C++", "C++11"}, 2011},{{"C++", "C++14"}, 2014},{{"C++", "C++17"}, 2017},{{"Java", "Java 7"}, 2011},{{"Java", "Java 8"}, 2014},{{"Java", "Java 9"}, 2017}};for (auto const &entry: unordered_map){auto key_pair = entry.first;std::cout << "{" << key_pair.first << "," << key_pair.second << "}, "<< entry.second << '\n';}return 0;
}
输出
{Java,Java 8}, 2014
{Java,Java 9}, 2017
{Java,Java 7}, 2011
{C++,C++17}, 2017
{C++,C++14}, 2014
{C++,C++11}, 2011
注意:boost的hash的位置改过,有网友说boost 1.72的hash在
#include <boost/container_hash/extensions.hpp>
原话是
By the way the functional hash has moved. I am not sure when, but in boost 1.72 it is in #include <boost/container_hash/extensions.hpp> I am not sure why the boost hash function for a tuple is not documented somewhere.
方法三:hash_combine
把下列代码放在任何你想实现本文目的代码头文件里
原话是
This works on gcc 4.5 allowing all c++0x tuples containing standard hashable types to be members of unordered_map and unordered_set without further ado. (I put the code in a header file and just include it.)
The function has to live in the std namespace so that it is picked up by argument-dependent name lookup (ADL).
#include <tuple>
namespace std{namespace{// Code from boost// Reciprocal of the golden ratio helps spread entropy// and handles duplicates.// See Mike Seymour in magic-numbers-in-boosthash-combine:// http://stackoverflow.com/questions/4948780template <class T>inline void hash_combine(std::size_t& seed, T const& v){seed ^= std::hash<T>()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);}// Recursive template code derived from Matthieu M.template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1>struct HashValueImpl{static void apply(size_t& seed, Tuple const& tuple){HashValueImpl<Tuple, Index-1>::apply(seed, tuple);hash_combine(seed, std::get<Index>(tuple));}};template <class Tuple>struct HashValueImpl<Tuple,0>{static void apply(size_t& seed, Tuple const& tuple){hash_combine(seed, std::get<0>(tuple));}};}template <typename ... TT>struct hash<std::tuple<TT...>> {size_toperator()(std::tuple<TT...> const& tt) const{ size_t seed = 0; HashValueImpl<std::tuple<TT...> >::apply(seed, tt); return seed; } };
}