В каком пространстве имен используются хеш-функции <T> для типов пользователей?
Мне непонятно, из стандарта С++ 11, где должны быть определены пользовательские функции hash<T>
.
Например, в 23.5.2 Заголовок <unordered_map>
он показывает:
template <class Key,
class T,
class Hash = hash<Key>,
class Pred = std::equal_to<Key>,
class Alloc = std::allocator<std::pair<const Key, T> > >
class unordered_map;
Это говорит о том, что по умолчанию в глобальном пространстве имен выполняется поиск hash<T>
, тогда как equal_to<>
выполняется поиск в пространстве имен std
.
Почему разница в пространстве имен между hash<>
и equal_to<>
?
(На самом деле, в описании http://www.cplusplus.com/reference/unordered_map/unordered_map/ ни одно из них не указывает пространство имен std
.)
Таким образом, при определении функтора hash<>
для пользовательского типа мы должны заключить его в блок namespace std { }
или он может оставаться в текущем пространстве имен?
Если код не имеет using namespace std;
, то как контейнеры STL, такие как unordered_map
, знают, что нужно искать в пространстве имен std
для предопределенных функторов hash<>
, связанных с примитивными типами? Похоже, что по умолчанию Hash = hash<Key>
не сможет найти их.
Извините, если это глупые вопросы.
Ответы
Ответ 1
Прежде всего, для шаблонов нет "зависимого от аргумента поиска". Поэтому hash<Key>
всегда будет ссылаться на один и тот же шаблон, либо в std
, либо в глобальном пространстве имен, независимо от Key
. Если бы он разрешил разные шаблоны в разных единицах перевода, это вызвало бы поведение undefined по нарушению ODR. Это само по себе предполагает, что hash
здесь означает std::hash
, то есть как-будто unordered_map
было объявлено следующим образом:
namespace std {
template<class T> struct hash;
template <class Key,
class T,
class Hash = hash<Key>, // resolves to std::hash<Key> for all Keys
class Pred = std::equal_to<Key>,
class Alloc = std::allocator<std::pair<const Key, T> > >
class unordered_map;
}
Однако типы, объявленные в стандартных заголовках, не должны быть записаны в источнике (они могут быть в принципе встроены в компилятор или предварительно скомпилированы какой-либо другой магией). Стандарт требует, чтобы каждый стандартный заголовок объявлял только типы в сводке, что означает, что, опуская объявление std::hash
, стандарт допускает некоторую гипотетическую реализацию, чтобы избежать вышеупомянутого загрязнения пространства имен. Это объясняет, почему вы не видите вышеуказанное объявление в резюме.
Для дальнейшего подтверждения вышеприведенного вывода переходим к §20.8.12. Хэш типа шаблона [unord.hash], который гласит:
Неупорядоченные ассоциативные контейнеры, определенные в 23.5, используют специализированные хэш-символы шаблона класса как хэш-функцию по умолчанию.
Этот параграф относится к std::hash
, который мы можем сделать из краткого описания <functional>
.
Нижняя строка: Это несоответствие стандартного форматирования. Есть много несоответствий, поэтому этот конкретный случай не удивляет вообще. В таких случаях нужно понять, что было предназначено, выведя единственную разумную вещь.
Специализация. Вы специализируетесь на шаблонах в пространстве имен, которое они объявили. Вам прямо предоставлено право специализировать стандартные шаблоны для вашего собственного типа:
namespace std {
template<> struct hash<YourClass> {
// specialization goes here
};
}
Ответ 2
когда вы хотите определить хэш-функтор для вашего типа, просто поместите его в ваше пространство имен и создайте unordered_xxx
с ним - довольно просто...
namespace my {
struct some_type {/*...*/};
struct some_hasher {/*...*/};
}
typedef std::unordered_map<int, my::some_type, my::some_hasher> my_some_hash_map;
AFAIK не рекомендуется (и, очевидно, небезопасно) добавлять smth в пространство имен std
. и на самом деле это не требуется (по моему опыту я не могу вспомнить, когда хочу добавить smth в std
- всегда можно решить "проблему", не делая этого)
И btw:
Это говорит о том, что по умолчанию в глобальном пространстве имен выполняется поиск хэша, тогда как в пространстве имен std выполняется поиск равным_te <.
НЕПРАВИЛЬНО! Рассмотрим это:
namespace my {
// See declaration of some_type above...
template <
typename SomeType = some_type
, typename Alloc = std::allocator<SomeType>
>
struct test;
}
очевидно, some_type
будет искать в текущем пространстве имен!
Таким образом, при определении функтора hash < > для пользовательского типа мы должны заключить его в блок namespace std { }
или он может оставаться в текущем пространстве имен?
Нет! Не пытайтесь уменьшить количество символов в типе, предоставив специализированную специализацию для std::hash<YourType>
- просто напишите свой собственный хэш-функтор в своем пространстве имен и добавьте его в качестве параметра шаблона при создании экземпляра std::unordered_xxx
...
Следуйте этому простому правилу: избегать добавления smth в namespace
, который не находится под вашим контролем (ВСЕ пространства имен! не только std
...) Кроме случаев, когда это необходимо (некоторой библиотекой) явно.