2013-08-31 3 views
0

Ключи в моем std::unordered_map являются boost::uuids::uuid s, поэтому 128-битные хэши считаются уникальными. Однако компилятор не может этого знать и поэтому говорит об этом.Как напрямую использовать ключ как хэш для std :: unordered_map?

error C2338: The C++ Standard doesn't provide a hash for this type. 

Как я могу использовать карту как хэши, как они есть? Кстати, std::size_t определяется как unsigned int __w64 в моей системе, что, я думаю, относится только к 64 бит.

+0

Также см. [Boost :: uuids :: uuid как ключ в std :: unordered_map?] (Https://stackoverflow.com/q/16471051/1708801) –

ответ

0

Я не нашел возможности использовать UUID как ключи для std::unordered_map, так как UUID имеет длину 128 бит, а хэш для карты - std::size_t, который может содержать только 64 бит.

Вместо этого я сбросил реальные 128-битные UUID только для 64-разрядных идентификаторов, которые могут храниться в типе uint64_t и поддерживаются контейнерами стандартной библиотеки.

1

вам необходимо предоставить хеш-функцию для типа boost::uuids::uuid. Поскольку он уникален, вы можете просто использовать идентификатор stl.

Здесь размещается объявление неупорядоченного_мапа.

template < class Key,         // unordered_map::key_type 
      class T,          // unordered_map::mapped_type 
      class Hash = hash<Key>,      // unordered_map::hasher 
      class Pred = equal_to<Key>,     // unordered_map::key_equal 
      class Alloc = allocator< pair<const Key,T> > // unordered_map::allocator_type 
      > class unordered_map; 
+0

Не могли бы вы привести пример того, как определить 'unordered_map' в моем случае? – danijar

0

Я думаю, что самый простой способ это осуществить специализацию std::hash для этого типов, который возвращает тот же вход:

namespace std 
{ 
    template<> 
    struct hash<Foo> 
    { 
     Foo operator(const Foo& foo) 
     { 
      return foo; 
     } 
    }; 
} 

предположив, что тип, Foo в примере, неявно конвертируются в std::size_t.

В вашем случае тип - это 128-битный GUID, а std::size_t использует 32 или 64 бит. Вы можете разбить 128-битный GUID в частях 64/32 бит и объединить значения.

+0

Тип, который вы назвали 'Foo', на самом деле является [' boost :: uuids :: uuid'] (http://www.boost.org/doc/libs/1_54_0/libs/uuid/uuid.html), который содержит 128 бит [GUID] (http://en.wikipedia.org/wiki/GUID). Думаю, в моей системе 'std :: size_t' содержит только 64 бита. – danijar

+1

Вы можете разбить число в двух частях 64 бит и комбинировать бит в хэш-порядке. См. Пример специализированной специализации std :: hash, который указан в ссылке cppreference.com, которую я предоставил. Он использует два хэш-значения из строк и объединяет их. – Manu343726

+0

Тогда мне не придется использовать 128-битные GUID. – danijar

2

Вам всегда нужно предоставить объект функции, сопоставляющий ключ с хэш-значением, даже если это сопоставление является идентификатором. Вы можете либо определить специализацию для std::hash<boost::uuids::uuid>, либо выбрать std::unordered_map<K, V> автоматически, или вы можете параметризовать неупорядоченную карту с дополнительным параметром шаблона для типа объекта функции. В дополнение к хэшу также нужна операция равенства, но по умолчанию используется operator==().

При этом хеш-значение не будет принимать 128-битное целое число, если ваша система не имеет встроенного 128-битного целочисленного типа. Хэш-значение должно быть std::size_t для использования со стандартными неупорядоченными контейнерами. Полный перечень требований к std::hash<T> специализации указан в 20.8.12 [unord.hash]:

  1. std::hash<X> должен быть по умолчанию конструктивно, копировать конструктивны и копировать переуступку.
  2. std::hash<X> необходимо заменить.
  3. Для типа ключа необходимо указать два вложенных типа argument_type и result_type для типа хешированного значения с последним, равным std::size_t.
  4. Для функции отношение k1 == k2 =>h(k1) == h(k2) должно быть правдой, где h - объект функции хэширования.

Итак, вам нужно будет определить что-то вдоль линий этого:

namespace std { 
    template <> 
    struct hash<boost::uuids::uuid> 
    { 
     typedef boost::uuids::uuid argument_type; 
     typedef std::size_t  result_type; 
     std::size_t operator()(boost::uuid::uuid key) const { 
      return transform_to_size_t(key); 
     } 
    }; 
} 

где transform_to_size_t() фактическое преобразование вам необходимо предоставить. };

+0

Если требуется гарантированная уникальность, вам необходимо убедиться, что биты size_t хэша также уникальны в вашем приложении. Ясно, что преобразование n-> 1 не может сохранить единственность. – OllieB

+0

@ OllieB: Я не уверен, о чем вы говорите. Если вы ссылаетесь на требование, чтобы хэш эквивалентных ключей был эквивалентен, вы не должны делать эту логическую операцию: очевидно, обратное неверно: 'h (k1) == h (k2)' не подразумевают, что 'k1 == k2'. –

+1

Kuhl: Я имел в виду вашу функцию transform_to_size_t. Это потенциально нарушает уникальность хэша. Например, 128 хэш, преобразованный в 32-битный хеш, дает 2^(128-32) столкновения из исходного 128-битного пространства UUID. Они могут быть или не быть значительными. – OllieB