Мы можем получить ответ, имитируя Boost и комбинируя хеши.
Предупреждение: Комбинирование хешей, то есть вычисление хеша многих вещей из множества хешей вещей, не является хорошимИдея в целом, так как результирующая хеш-функция не является «хорошей» в статистическом смысле.Надлежащий хэш многих вещей должен быть построен из целых необработанных данных всех компонентов, а не из промежуточных хешей.Но в настоящее время нет хорошего стандартного способа сделать это.
В любом случае:
Прежде всего, нам нужна функция hash_combine
.По независящим от меня причинам он не был включен в стандартную библиотеку, но он является центральным элементом всего остального:
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);
}
Используя это, мы можем хэшировать все, что состоит из хешируемых элементов, в частности пары и кортежи(упражнение для читателя).
Однако мы также можем использовать это для хеширования контейнеров путем хеширования их элементов.Это именно то, что делает "хэш диапазона" в Boost, но просто сделать это самостоятельно, используя функцию объединения.
Как только вы закончите написание хеша диапазона, просто специализируйте std::hash
и вы 'хорошо бы пойти:
namespace std
{
template <typename T, class Comp, class Alloc>
struct hash<std::set<T, Comp, Alloc>>
{
inline std::size_t operator()(const std::set<T, Comp, Alloc> & s) const
{
return my_range_hash(s.begin(), s.end());
}
};
/* ... ditto for other containers */
}
Если вы хотите подражать хорошему принтеру, вы могли бы даже сделать что-то более экстремальное и специализировать std::hash
для всех контейнеров, но я, вероятно, был бы более осторожен с этим исоздать явный хеш-объект для контейнеров:
template <typename C> struct ContainerHasher
{
typedef typename C::value_type value_type;
inline size_t operator()(const C & c) const
{
size_t seed = 0;
for (typename C::const_iterator it = c.begin(), end = c.end(); it != end; ++it)
{
hash_combine<value_type>(seed, *it);
}
return seed;
}
};
Использование:
std::unordered_map<std::set<int>, std::string, ContainerHasher<std::set<int>>> x;