C++0x adds hash<...>(...).
I could not find a hash_combine function though, as presented in boost. What is the cleanest way to implement something like this? Perhaps, using C++0x xor_combine?
C++0x adds hash<...>(...).
I could not find a hash_combine function though, as presented in boost. What is the cleanest way to implement something like this? Perhaps, using C++0x xor_combine?
Well, just do it like the boost guys did it:
template <class T> inline void hash_combine(std::size_t& seed, const T& v) { std::hash<T> hasher; seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); } std::pair (or tuple, even). It would compute the hash of each element, then combine them. (And in the spirit of the standard library, in an implementation defined way.)I'll share it here since it can be useful to others looking for this solution: starting from @KarlvonMoor answer, here's a variadic template version, which is terser in its usage if you have to combine several values together:
inline void hash_combine(std::size_t& seed) { } template <typename T, typename... Rest> inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) { std::hash<T> hasher; seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); hash_combine(seed, rest...); } Usage:
std::size_t h=0; hash_combine(h, obj1, obj2, obj3); This was written originally to implement a variadic macro to easily make custom types hashable (which I think is one of the primary usages of a hash_combine function):
#define MAKE_HASHABLE(type, ...) \ namespace std {\ template<> struct hash<type> {\ std::size_t operator()(const type &t) const {\ std::size_t ret = 0;\ hash_combine(ret, __VA_ARGS__);\ return ret;\ }\ };\ } Usage:
struct SomeHashKey { std::string key1; std::string key2; bool key3; }; MAKE_HASHABLE(SomeHashKey, t.key1, t.key2, t.key3) // now you can use SomeHashKey as key of an std::unordered_map I really like the C++17 approach from the answer by vt4a2h, however it suffers from a problem: The Rest is passed on by value whereas it would be more desirable to pass them on by const references (which is a must if it shall be usable with move-only types).
Here is the adapted version which still uses a fold expression (which is the reason why it requires C++17 or above) and uses std::hash (instead of the Qt hash function):
template <typename T, typename... Rest> void hash_combine(std::size_t& seed, const T& v, const Rest&... rest) { seed ^= std::hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); (hash_combine(seed, rest), ...); } For completeness sake: All the types which shall be usable with this version of hash_combine must have a template specialization for hash injected into the std namespace.
Example:
namespace std // Inject hash for B into std:: { template<> struct hash<B> { std::size_t operator()(B const& b) const noexcept { std::size_t h = 0; cgb::hash_combine(h, b.firstMember, b.secondMember, b.andSoOn); return h; } }; } So that type B in the example above is also usable within another type A, like the following usage example shows:
struct A { std::string mString; int mInt; B mB; B* mPointer; } namespace std // Inject hash for A into std:: { template<> struct hash<A> { std::size_t operator()(A const& a) const noexcept { std::size_t h = 0; cgb::hash_combine(h, a.mString, a.mInt, a.mB, // calls the template specialization from above for B a.mPointer // does not call the template specialization but one for pointers from the standard template library ); return h; } }; } Hash template arguments of the standard containers to specify your custom hasher instead of injecting it into the std namespace.A few days ago I came up with slightly improved version of this answer (C++ 17 support is required):
template <typename T, typename... Rest> void hashCombine(uint& seed, const T& v, Rest... rest) { seed ^= ::qHash(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); (hashCombine(seed, rest), ...); } The code above is better in terms of code generation. I used qHash function from Qt in my code, but it's also possible to use any other hashers.
(int[]){0, (hashCombine(seed, rest), 0)...}; and it'll also work in C++11.The answer by vt4a2h is certainly nice but uses the C++17 fold expression and not everyone is able to switch to a newer toolchain easily. The version below uses the expander trick to emulate a fold expression and works in C++11 and C++14 as well.
Additionally, I marked the function inline and use perfect forwarding for the variadic template arguments.
template <typename T, typename... Rest> inline void hashCombine(std::size_t &seed, T const &v, Rest &&... rest) { std::hash<T> hasher; seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); (int[]){0, (hashCombine(seed, std::forward<Rest>(rest)), 0)...}; } This could also be solved by using a variadic template as follows:
#include <functional> template <typename...> struct hash; template<typename T> struct hash<T> : public std::hash<T> { using std::hash<T>::hash; }; template <typename T, typename... Rest> struct hash<T, Rest...> { inline std::size_t operator()(const T& v, const Rest&... rest) { std::size_t seed = hash<Rest...>{}(rest...); seed ^= hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); return seed; } }; Usage:
#include <string> int main(int,char**) { hash<int, float, double, std::string> hasher; std::size_t h = hasher(1, 0.2f, 2.0, "Hello World!"); } One could certainly make a template function, but this could cause some nasty type deduction e.g hash("Hallo World!") will calculate a hash value on the pointer rather than on the string. This is probably the reason, why the standard uses a struct.
Many of the answers are based on the old implementation of boost::hash_combine. Here's a solution that uses the new boost::hash_combine mixing functions:
#include <functional> #include <limits> // Borrowed from Boost.ContainerHash // https://github.com/boostorg/container_hash/blob/ee5285bfa64843a11e29700298c83a37e3132fcd/include/boost/container_hash/hash.hpp#L471 template <typename T> void hash_combine(std::size_t& seed, const T& v) { static constexpr auto digits = std::numeric_limits<std::size_t>::digits; static_assert(digits == 64 || digits == 32); if constexpr (digits == 64) { // https://github.com/boostorg/container_hash/blob/ee5285bfa64843a11e29700298c83a37e3132fcd/include/boost/container_hash/detail/hash_mix.hpp#L67 std::size_t x = seed + 0x9e3779b9 + std::hash<T>()(v); const std::size_t m = 0xe9846af9b1a615d; x ^= x >> 32; x *= m; x ^= x >> 32; x *= m; x ^= x >> 28; seed = x; } else { // 32-bits // https://github.com/boostorg/container_hash/blob/ee5285bfa64843a11e29700298c83a37e3132fcd/include/boost/container_hash/detail/hash_mix.hpp#L88 std::size_t x = seed + 0x9e3779b9 + std::hash<T>()(v); const std::size_t m1 = 0x21f0aaad; const std::size_t m2 = 0x735a2d97; x ^= x >> 16; x *= m1; x ^= x >> 15; x *= m2; x ^= x >> 15; seed = x; } } I leave it as an exercise to the reader to write a variadic template version that uses expression folding.
The answer by Henri Menke works great, but if you treat warnings as errors with for example:
add_compile_options(-Werror) GCC 9.3.0 will give this error:
Test.h:223:67: error: ISO C++ forbids compound-literals [-Werror=pedantic] 223 | (int[]){0, (hashCombine(seed, std::forward<Rest>(rest)), 0)...}; | ^ cc1plus: all warnings being treated as errors We can update the code to avoid the error like this:
template <typename T, typename... Rest> inline void hashCombine(std::size_t &seed, T const &v, Rest &&... rest) { std::hash<T> hasher; seed ^= (hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2)); int i[] = { 0, (hashCombine(seed, std::forward<Rest>(rest)), 0)... }; (void)(i); } You can use the rst C++ library that I developed to do that:
#include "rst/stl/hash.h" struct Point { Point(const int x, const int y) : x(x), y(y) {} int x = 0; int y = 0; }; bool operator==(const Point lhs, const Point rhs) { return (lhs.x == rhs.x) && (lhs.y == rhs.y); } namespace std { template <> struct hash<Point> { size_t operator()(const Point point) const { return rst::HashCombine({point.x, point.y}); } }; }