В каком пространстве имен используются хеш-функции <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...) Кроме случаев, когда это необходимо (некоторой библиотекой) явно.