Как объединить хэш-значения в C++0x?


C++0x добавляет hash<...>(...).

Я не смог найти функцию hash_combine, хотя, как представлено в boost. Каков самый чистый способ реализовать что-то подобное? Возможно, используя C++0x xor_combine?

3 65

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!") вычислит хэш-значение на указателе, а не на строке. Это, вероятно, причина, почему стандарт использует структуру.