Причина проста, она не была добавлена к стандарту. То же самое относится к хэшированию других структур, таких как tuple
.
Вещи, как правило, добавляются к стандарту, когда они достаточно хороши, а не когда они совершенны, поскольку совершенство является врагом добра. Больше специализации std::hash
- это не то, что может сломать код (что часто), поэтому добавление новых является относительно безвредным.
В любом случае, с этой целью мы можем написать наши собственные хэш-расширения. В качестве примера:
namespace hashers {
constexpr size_t hash_combine(size_t, size_t); // steal from boost, or write your own
constexpr size_t hash_combine(size_t a) { return a; }
constexpr size_t hash_combine() { return 0; }
template<class...Sizes>
constexpr size_t hash_combine(size_t a, size_t b, Sizes... sizes) {
return hash_combine(hash_combine(a,b), sizes...);
}
template<class T=void> struct hash;
template<class A, class B>
constexpr size_t custom_hash(std::pair<A,B> const& p) {
return hash_combine(hash<size_t>{}(2), hash<std::decay_t<A>>{}(p.first), hash<std::decay_t<B>>{}(p.second));
}
template<class...Ts, size_t...Is>
constexpr size_t custom_hash(std::index_sequence<Is...>, std::tuple<Ts...> const& p) {
return hash_combine(hash<size_t>{}(sizeof...(Ts)), hash<std::decay_t<Ts>>{}(std::get<Is>(p))...);
}
template<class...Ts>
constexpr size_t custom_hash(std::tuple<Ts...> const& p) {
return custom_hash(std::index_sequence_for<Ts...>{}, p);
}
template<class T0, class C>
constexpr size_t custom_hash_container(size_t n, C const& c) {
size_t retval = hash<size_t>{}(n);
for(auto&& x : c)
retval = hash_combine(retval, hash<T>{}(x));
return retval;
}
template<class T0, class C>
constexpr size_t custom_hash_container(C const& c) {
return custom_hash_container(c.size(), c);
}
template<class T, class...Ts>
size_t custom_hash(std::vector<T, Ts...> const& v) {
return custom_hash_container<T>(v);
}
template<class T, class...Ts>
size_t custom_hash(std::basic_string<T, Ts...> const& v) {
return custom_hash_container<T>(v);
}
template<class T, size_t n>
constexpr size_t custom_hash(std::array<T, n> const& v) {
return custom_hash_container<T>(n, v);
}
template<class T, size_t n>
constexpr size_t custom_hash(T (const& v)[n]) {
return custom_hash_container<T>(n, v);
}
// etc -- list, deque, map, unordered map, whatever you want to support
namespace details {
template<class T, class=void>
struct hash : std::hash<T> {};
using hashers::custom_hash;
template<class T>
struct hash<T,decltype(void(
custom_hash(declval<T const&>())
)) {
constexpr size_t operator()(T const& t)const {
return custom_hash(t);
}
};
}
template<class T>
struct hash : details::hash<T> {};
template<>
struct hash<void> {
template<class T>
constexpr size_t operator()(T const& t)const { return hash<T>{}(t); }
}
}
и теперь hashers::hash<T>
будет рекурсивен использовать либо ADL-ищутся custom_hash
функцию или std::hash
, если это не удается, хэш T
и ее компонентов, а также hashers::hash<>
является универсальной мясорубкой, который пытается хэш ничего перешел к нему.
Код не может компилироваться, как показано на рисунке.
Я выбрал хэш всех контейнеров и кортежей как хэш их длины, а затем хеширует комбинацию их содержимого. В качестве побочного эффекта array<int, 3>
хешируют то же, что и tuple<int,int,int>
, и tuple<int,int>
хешируют то же, что и pair<int,int>
, и std::vector<char>{'a','b','c', '\0'}
хешируют то же, что и "abc"
, что, я думаю, является прекрасным свойством. Пустые массивы/tuple/vector/etc хэши, такие как size_t(0)
.
Вы можете расширить вышеуказанную систему для собственных типов, просто перекрывая custom_hash
в пространстве имен рассматриваемого типа, или специализирующийся либо std::hash<X>
или hashers::hash<X>
, чтобы сделать свой собственный хэш (я бы с std::hash
по принципу наименьшего удивления себя). Для расширенного использования вы можете специализировать hashers::details::hash<X,void>
с SFINAE, но я бы сказал, сделайте это для custom_hash
.
Можете ли вы hash 'const myClass *'? Или вы можете использовать hash 'const myClass'? –
Существует [предложение] (http://isocpp.org/files/papers/n3980.html), в котором рассматривается эта проблема. Помимо этого, этот вопрос слишком широк и не отвечает на него. Пока вы можете предоставить свою собственную 'std :: hash' специализацию и использовать' boost :: hash_combine' для объединения отдельных хэшей. – Praetorian
Пожалуйста, не стесняйтесь использовать программное обеспечение, стоящее за предложением, которое поддерживает Praetorian: https://github.com/HowardHinnant/hash_append Только за последние пару дней моя команда выиграла от возможности легко сравнивать хэш-алгоритм X с алгоритмом хэша Y , применительно к нашему конкретному приложению, и выберите, какой из них лучше для нас. –