Для поддержки ключевых типов , определяемых пользователем в std::unordered_set<Key>
и std::unordered_map<Key, Value>
один должен обеспечивать operator==(Key, Key)
и хэш - функтор:
struct X { int id; /* ... */ };
bool operator==(X a, X b) { return a.id == b.id; }
struct MyHash {
size_t operator()(const X& x) const { return std::hash<int>()(x.id); }
};
std::unordered_set<X, MyHash> s;
Было бы удобнее писать просто std::unordered_set<X>
с хешем по умолчанию для типа X
, как для типов, поставляемых вместе с компилятором и библиотекой. После консультации
- Проект стандарта C ++ N3242 §20.8.12 [unord.hash] и §17.6.3.4 [hash.requirements],
- Boost.Unordered
- g ++
include\c++\4.7.0\bits\functional_hash.h
- VC10
include\xfunctional
- различные связанные вопросы в Stack Overflow
кажется возможным специализироваться std::hash<X>::operator()
:
namespace std { // argh!
template <>
inline size_t
hash<X>::operator()(const X& x) const { return hash<int>()(x.id); } // works for MS VC10, but not for g++
// or
// hash<X>::operator()(X x) const { return hash<int>()(x.id); } // works for g++ 4.7, but not for VC10
}
Учитывая, что компилятор поддерживает C ++ 11, пока что экспериментальный --- я не пробовал Clang ---, вот мои вопросы:
Законно ли добавлять такую специализацию в пространство имен
std
? У меня смешанные чувства по этому поводу.Какая из
std::hash<X>::operator()
версий, если таковые имеются, соответствует стандарту C ++ 11?Есть ли портативный способ сделать это?
источник
operator==(const Key, const Key)
std::hash
(в отличие от других вещей вstd
пространстве имен) не поощряется руководством по стилю Google ; воспринимайте это с недоверием.Ответы:
Вам прямо разрешено и рекомендуется добавлять специализации в пространство имен
std
*. Правильный (и в основном единственный) способ добавить хеш-функцию таков:(Другими популярными специализациями, которые вы можете поддержать, являются
std::less
,std::equal_to
иstd::swap
.)*) полагаю, если один из задействованных типов определяется пользователем.
источник
unorder_map<eltype, hash, equality>
вместо этого создание экземпляра , чтобы не испортить кому-то день забавным ADL-бизнесом. ( Отредактируйте совет Пита Беккера по этой теме )operator==
.) Моя общая философия заключается в том, что если функция является естественной и по существу единственно «правильной» (например, сравнение лексикографических пар), я добавляю ее кstd
. Если это что-то особенное (например, сравнение неупорядоченных пар), я делаю это специфичным для типа контейнера.Я бы сделал ставку на аргумент шаблона Hash для классов unordered_map / unorder_set / ...:
Конечно
struct Xhasher { size_t operator(const X&) const; };
)std::hash<X>()
источник
std::hash
по-прежнему являетсяchar*
!hash
специализация мешает через ADL? Я имею в виду, это вполне правдоподобно, но мне сложно придумать проблемный случай.std::unordered_map<Whatever, Xunset>
и это не работает, потому что вашXunset
тип хешера не может быть сконструирован по умолчанию.@Kerrek SB рассмотрел пункты 1) и 3).
2) Несмотря на то, что g ++ и VC10 объявляются
std::hash<T>::operator()
с разными подписями, обе реализации библиотеки соответствуют стандарту.Стандарт не определяет членов
std::hash<T>
. Он просто говорит, что каждая такая специализация должна удовлетворять тем же требованиям «хеширования», которые необходимы для второго аргумента шаблонаstd::unordered_set
и так далее. А именно:H
- это объект функции, по крайней мере, с одним типом аргументаKey
.H
копирует конструкцию.H
разрушаемо.h
- выражение типаH
илиconst H
, аk
выражение типа, конвертируемого в (возможноconst
)Key
, тоh(k)
является допустимым выражением с типомsize_t
.h
является выражением типаH
илиconst H
, иu
является l-значением типаKey
, тоh(u)
является допустимым выражением с типом,size_t
который не изменяетсяu
.источник
std::hash<X>::operator()
а неstd::hash<X>
в целом, а сигнатураstd::hash<T>::operator()
определяется реализацией.