Това работи на gcc 4.5, позволявайки на всички c++0x кортежи, съдържащи стандартни хешируеми типове, да бъдат членове на unordered_map
и unordered_set
без повече шум. (Поставям кода в заглавен файл и просто го включвам.)
Функцията трябва да живее в пространството на имената std, така че да бъде взета от аргумент-зависимо търсене на име (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/4948780
template <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_t
operator()(std::tuple<TT...> const& tt) const
{
size_t seed = 0;
HashValueImpl<std::tuple<TT...> >::apply(seed, tt);
return seed;
}
};
}
Стандартен код за съответствие
Yakk посочва, че специализирането на неща в пространството от имена std всъщност е недефинирано поведение. Ако желаете да имате решение, отговарящо на стандартите, тогава трябва да преместите целия този код във вашето собствено пространство от имена и да се откажете от всякаква идея ADL да намира правилното внедряване на хеш автоматично. Вместо :
unordered_set<tuple<double, int> > test_set;
Имате нужда от:
unordered_set<tuple<double, int>, hash_tuple::hash<tuple<double, int>>> test2;
където hash_tuple
е вашето собствено пространство от имена, а не std::
.
За да направите това, първо трябва да декларирате реализация на хеш вътре в пространството от имена hash_tuple
. Това ще препрати всички типове без кортежи към std::hash
:
namespace hash_tuple{
template <typename TT>
struct hash
{
size_t
operator()(TT const& tt) const
{
return std::hash<TT>()(tt);
}
};
}
Уверете се, че hash_combine
се обажда на hash_tuple::hash
, а не на std::hash
namespace hash_tuple{
namespace
{
template <class T>
inline void hash_combine(std::size_t& seed, T const& v)
{
seed ^= hash_tuple::hash<T>()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
}
След това включете целия друг предишен код, но го поставете вътре namespace hash_tuple
, а не std::
namespace hash_tuple{
namespace
{
// 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_t
operator()(std::tuple<TT...> const& tt) const
{
size_t seed = 0;
HashValueImpl<std::tuple<TT...> >::apply(seed, tt);
return seed;
}
};
}
person
Leo Goodstadt
schedule
18.08.2011