Общий хеш для кортежей в unordered_map / unordered_set

Почему не std::unordered_map<tuple, string> просто работает из коробки? Достаточно определить hash-функцию для tuple , например

 template struct do_hash<tuple> { size_t operator()(std::tuple const& tt) const {...} }; 

Построение неупорядоченной карты с кортежами в качестве ключей (Matthieu M.) показывает, как автоматизировать это для boost::tuple . Есть ли способ сделать это для c ++ 0x кортежей без использования вариативных шаблонов?

Конечно, это должно быть в стандарте 🙁

Это работает на gcc 4.5, позволяя всем кортежам c ++ 0x, содержащим стандартные типы хеширования, быть членами unordered_map и unordered_set без дальнейших церемоний. (Я помещаю код в файл заголовка и просто включаю его.)

Функция должна жить в пространстве имен std, чтобы ее подхватили зависящие от аргументов имена (ADL).

Есть ли более простое решение?

 #include  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  inline void hash_combine(std::size_t& seed, T const& v) { seed ^= std::hash()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); } // Recursive template code derived from Matthieu M. template ::value - 1> struct HashValueImpl { static void apply(size_t& seed, Tuple const& tuple) { HashValueImpl::apply(seed, tuple); hash_combine(seed, std::get(tuple)); } }; template  struct HashValueImpl { static void apply(size_t& seed, Tuple const& tuple) { hash_combine(seed, std::get<0>(tuple)); } }; } template  struct hash> { size_t operator()(std::tuple const& tt) const { size_t seed = 0; HashValueImpl >::apply(seed, tt); return seed; } }; } Пространство #include  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  inline void hash_combine(std::size_t& seed, T const& v) { seed ^= std::hash()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); } // Recursive template code derived from Matthieu M. template ::value - 1> struct HashValueImpl { static void apply(size_t& seed, Tuple const& tuple) { HashValueImpl::apply(seed, tuple); hash_combine(seed, std::get(tuple)); } }; template  struct HashValueImpl { static void apply(size_t& seed, Tuple const& tuple) { hash_combine(seed, std::get<0>(tuple)); } }; } template  struct hash> { size_t operator()(std::tuple const& tt) const { size_t seed = 0; HashValueImpl >::apply(seed, tt); return seed; } }; } 

Стандартный согласованный код

Якк указывает, что специализация в пространстве имен std – это фактически неопределенное поведение. Если вы хотите иметь стандартное решение, то вам нужно переместить весь этот код в ваше собственное пространство имен и отказаться от идеи ADL о поиске правильной реализации hashа автоматически. Вместо :

 unordered_set > test_set; 

Тебе нужно:

 unordered_set, hash_tuple::hash>> test2; 

где hash_tuple – ваше собственное пространство имен, а не std:: .

Для этого сначала нужно объявить hash-реализацию внутри пространства имен hash_tuple . Это приведет к переходу всех типов без кортежей в std::hash :

 namespace hash_tuple{ template  struct hash { size_t operator()(TT const& tt) const { return std::hash()(tt); } }; } 

Убедитесь, что hash_combine вызывает hash_tuple::hash а не std::hash

 namespace hash_tuple{ namespace { template  inline void hash_combine(std::size_t& seed, T const& v) { seed ^= hash_tuple::hash()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); } } Пространство namespace hash_tuple{ namespace { template  inline void hash_combine(std::size_t& seed, T const& v) { seed ^= hash_tuple::hash()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); } } 

Затем namespace hash_tuple весь другой предыдущий код, но поместите его в namespace hash_tuple а не std::

 namespace hash_tuple{ namespace { // Recursive template code derived from Matthieu M. template ::value - 1> struct HashValueImpl { static void apply(size_t& seed, Tuple const& tuple) { HashValueImpl::apply(seed, tuple); hash_combine(seed, std::get(tuple)); } }; template  struct HashValueImpl { static void apply(size_t& seed, Tuple const& tuple) { hash_combine(seed, std::get<0>(tuple)); } }; } template  struct hash> { size_t operator()(std::tuple const& tt) const { size_t seed = 0; HashValueImpl >::apply(seed, tt); return seed; } }; } Пространство namespace hash_tuple{ namespace { // Recursive template code derived from Matthieu M. template ::value - 1> struct HashValueImpl { static void apply(size_t& seed, Tuple const& tuple) { HashValueImpl::apply(seed, tuple); hash_combine(seed, std::get(tuple)); } }; template  struct HashValueImpl { static void apply(size_t& seed, Tuple const& tuple) { hash_combine(seed, std::get<0>(tuple)); } }; } template  struct hash> { size_t operator()(std::tuple const& tt) const { size_t seed = 0; HashValueImpl >::apply(seed, tt); return seed; } }; } 
 #include  #include  namespace std { template struct hash> { size_t operator()(tuple const& arg) const noexcept { return boost::hash_value(arg); } }; } 

В моем проекте C ++ 0x 20.8.15 говорит, что хеш специализирован для встроенных типов (включая указатели, но, похоже, не подразумевает разыменование их). Он также, как представляется, специализируется на error_code , bitset , unique_ptr , shared_ptr , typeindex , string , u16string , u32string , wstring , vector и thread::id . (расшифровка списка!)

Я не использовал вариации C ++ 0x, поэтому мое форматирование, вероятно, далеко, но что-то в этом направлении может работать для всех кортежей.

 size_t hash_combiner(size_t left, size_t right) //replacable { return left + 0x9e3779b9 + (right<<6) + (right>>2);} template struct hash_impl { size_t operator()(size_t a, const std::tuple& t) const { typedef typename std::tuple_element>::type nexttype; hash_impl next; size_t b = std::hash()(std::get(t)); return next(hash_combiner(a, b), t); } }; template struct hash_impl<0, types...> { size_t operator()(size_t a, const std::tuple& t) const { typedef typename std::tuple_element<0, std::tuple>::type nexttype; size_t b = std::hash()(std::get<0>(t)); return hash_combiner(a, b); } }; template struct tuple_hash> { size_t operator()(const std::tuple& t) { const size_t begin = std::tuple_size>::value-1; return hash_impl()(0, t); } } 

Эта версия фактически компилируется и запускается

Якк отметил, что специализация std::hash технически не допускается, поскольку мы специализируемся на стандартном шаблоне библиотеки с объявлением, которое не зависит от пользовательского типа.

Interesting Posts

getClass (). getClassLoader () имеет значение null, почему?

Java: как получить входные данные из System.console ()

Смыкая свободную функцию

Как импортировать каталог электронной почты эволюции на новый компьютер?

Неожиданная ошибка «else» в «else»

Lost Mac DVD, как я могу получить новую копию диска?

Объяснение сильного и слабого хранения в iOS5

Отправить письмо с помощью java

Предварительный просмотр второго экрана в главном

Как сгладить список в список без принуждения?

Не удается загрузить Windows с основного жесткого диска после форматирования диска C в Ubuntu

Системная и сжатая память и системные прерывания занимают 40% от процессора

Как установить программу или приложение по умолчанию для обработки определенного типа файла или протокола URL?

Как удалить базу данных с помощью Mongoose?

Android-эмулятор спама logcat с «Service com.android.exchange.ExchangeService просочился ServiceConnection …, который был первоначально связан здесь

Давайте будем гением компьютера.