Таким образом, ваша основная проблема заключается в том, что у вас нет хэшера для std::tuple
-- стандартная библиотека в настоящее время не поставляется с ним по умолчанию. Я думаю, что это упущение.
Вы не можете добавить хэш по умолчанию для std::tuple
— ни для всех tuple
, ни для вашего конкретного списка — потому что стандарт говорит, что вам не разрешено:
17.6.4.2.1 Пространство имен std [namespace.std]/1 Поведение программы C++ не определено, если она добавляет объявления или определения в пространство имен std или в пространство имен внутри пространства имен std, если не указано иное. Программа может добавить специализацию шаблона для любого шаблона стандартной библиотеки в пространство имен std только в том случае, если объявление зависит от определяемого пользователем типа и специализация соответствует требованиям стандартной библиотеки для исходного шаблона и не запрещена явно.
Таким образом, вам остается либо написать собственный хэш для вашего типа tuple
и передать его вашему unordered_map
, либо использовать тип, который не является просто std::tuple
для вашего ключа, либо написать собственный хэш, который поддерживает каждый tuple
(включая рекурсивные tuple
s) а также поддерживает поиск hash<T>
для не-tuple
s. Четвертый вариант — написать свою собственную хэш-систему на основе ADL и настроить ее так, чтобы типы с допустимыми специализациями std::hash
использовали ее с различными другими резервными вариантами.
Я проиллюстрирую 3-й вариант выше.
Во-первых, вы хотите иметь возможность комбинировать два хэш-значения таким образом, чтобы это не приводило к множеству ложных коллизий.
inline std::size_t hash_result_combine(std::size_t lhs, std::size_t rhs)
{
return lhs ^( rhs + 0x9e3779b9 + (lhs<<6) + (lhs>>2));
}
это берет два выхода hash
и объединяет их. Затем мы можем связать это любое количество раз. (константы заимствованы из https://stackoverflow.com/a/7111460/1774667)
Далее немного шаблонного индексирования. Для большинства общих работ с std::tuple
требуются следующие вещи:
template<unsigned...>struct indexes{};
template<unsigned Max, unsigned... Is>struct make_indexes:make_indexes<Max-1,Max-1,Is...> {};
template<unsigned...Is>struct make_indexes<0,Is...>:indexes<Is...>{};
Наконец, класс хеширования, который работает почти со всеми типами:
struct my_hasher {
template<typename... Args>
std::size_t operator()(indexes<>, std::tuple<Args...> const& tup) const {
return 0;
}
template<unsigned I, unsigned... Is,typename... Args>
std::size_t operator()(indexes<I, Is...>,std::tuple<Args...> const& tup) const {
return hash_result_combine( (*this)(std::get<I>(tup)), (*this)(indexes<Is...>, tup) );
}
template<typename... Args>
std::size_t operator()(std::tuple<Args...> const& tup) const {
return (*this)(make_indexes<sizeof...(Args)>(), tup);
}
template<typename T>
std::size_t operator()(T const& t) const { return std::hash<T>()(t); }
};
вы можете передать my_hasher
в unordered_set
вместо hash<T>
для tuple
s, tuple
s, содержащих кортежи, или даже для скаляров - это очень щедро.
typedef std::unordered_map<const key_t,IndexGuide, my_hasher> GuideDouble;
и теперь мы правильно хэшируем key_t
.
person
Yakk - Adam Nevraumont
schedule
04.03.2014
const
изconst key_t
вtypedef
; это не должно быть там. - person Angew is no longer proud of SO   schedule 04.03.2014