Как объединить хэш-значения в C++0x?
C++0x добавляет hash<...>(...)
.
Я не смог найти функцию hash_combine, хотя, как представлено в boost. Каков самый чистый способ реализовать что-то подобное? Возможно, используя C++0x xor_combine
?
3 ответа:
ну, просто сделайте это, как ребята boost сделали это:
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); }
я поделюсь им здесь, так как это может быть полезно для других, ищущих это решение: начиная с @KarlvonMoor ответ, вот версии шаблонов с переменным числом аргументов, которая лаконичнее в его использовании, если вам нужно объединить несколько значений:
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...); }
использование:
std::size_t h=0; hash_combine(h, obj1, obj2, obj3);
Это было первоначально написано, чтобы реализовать вариативную макрос легко сделать пользовательские типы hashable (который я думаю, является одним из основных использований
hash_combine
функция):#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;\ }\ };\ }
использование:
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
Это также можно решить с помощью вариационного шаблона следующим образом:
#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; } };
использование:
#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!"); }
можно, конечно, сделать функцию шаблона, но это может вызвать некоторые неприятные типа дедукции, например
hash("Hallo World!")
вычислит хэш-значение на указателе, а не на строке. Это, вероятно, причина, почему стандарт использует структуру.