Home > Article > Backend Development > How to Make `std::unordered_map` Work Without Defining a Custom Hash Function?
Generic Hash for Tuples in unordered_map/unordered_set
Q: Why doesn't std::unordered_map
In standard C , to use tuples as keys in associative containers like unordered_map or unordered_set, you need to define a custom hash function.
Q: Can this be automated for C 0x tuples without using variadic templates?
Yes, utilizing the following code:
namespace std{ namespace { template <class T> inline void hash_combine(std::size_t& seed, T const& v) { seed ^= std::hash<T>()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); } 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_t operator()(std::tuple<TT...> const& tt) const { size_t seed = 0; HashValueImpl<std::tuple<TT...> >::apply(seed, tt); return seed; } }; }
Q: Is there a simpler solution?
Standard Non-Conforming Solution (ADL Enabled):
#includenamespace std{ namespace { template <class T> inline void hash_combine(std::size_t& seed, T const& v) { seed ^= std::hash<T>()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); } 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_t operator()(std::tuple<TT...> const& tt) const { size_t seed = 0; HashValueImpl<std::tuple<TT...> >::apply(seed, tt); return seed; } }; }
Standard Conforming Solution (No ADL):
To achieve strict standard conformance, you must move the above code into a separate namespace (e.g., hash_tuple) and modify the syntax to specify the custom hash function explicitly.
namespace hash_tuple{ // Forward non-tuple types to std::hash template <typename TT> struct hash { size_t operator()(TT const& tt) const { return std::hash<TT>()(tt); } }; }
Replace hash_combine and HashValueImpl from the non-conforming solution with their hash_tuple counterparts. Finally, use the following syntax:
unordered_set<tuple<double, int>, hash_tuple::hash<tuple<double, int>>> test2;
The above is the detailed content of How to Make `std::unordered_map` Work Without Defining a Custom Hash Function?. For more information, please follow other related articles on the PHP Chinese website!