Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In which namespace do hash<T> functors for user types belong?

It's unclear to me from the C++11 standard where user-defined hash<T> functors should be defined.

For example, in 23.5.2 Header <unordered_map>, it shows:

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;

This suggests that, by default, hash<T> is searched for in the global namespace, whereas equal_to<> is searched for in the std namespace.

Why the difference in namespace between hash<> and equal_to<>?

(Actually, in the description at http://www.cplusplus.com/reference/unordered_map/unordered_map/, neither specifies the std namespace.)

Thus, when defining a hash<> functor for a user type, should we enclose it within a namespace std { } block, or can it remain in the current namespace?

If the code does not have a using namespace std;, how do the STL containers like unordered_map know to look in the std namespace for the predefined hash<> functors associated with the primitive types? It seems like the default Hash = hash<Key> would fail to find these.

Sorry if these are stupid questions..

like image 438
Hugues Avatar asked Feb 03 '13 06:02

Hugues


1 Answers

First of all, there is no "argument dependent lookup" for templates. So hash<Key> will always refer to the same template, either in std or in global namespace, independent of Key. If it had resolved to different templates in different translation units, it would cause undefined behavior by ODR violation. This alone suggests that hash here means std::hash, that is as-if unordered_map was declared like this:

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;
}

However, the types declared in the standard headers are not required to be written in source (they could be in principle built-in to the compiler or pre-compiled by some other magic). The standard requires each standard header to declare only the types in its synopsis, which means that by omitting the std::hash declaration the standard permits some hypothetical implementation to avoid the above namespace pollution. This explains why you do not see the above declaration in the synopsis.

To further back-up the above conclusion, we go to §20.8.12 Class template hash [unord.hash] that reads:

The unordered associative containers defined in 23.5 use specializations of the class template hash as the default hash function.

This paragraph refers to the std::hash, which we can infer from the synopsis of <functional>.

Bottom line: This is an inconsistency in the standard formatting. There are plenty of inconsistencies, so this specific case is not surprising at all. In such cases one has to understand what has been intended by deducing what is the only sensible thing.

Specialization. You specialize templates in the namespace that they were declared. You are explicitly granted the right to specialize standard templates for your own type:

namespace std {
    template<> struct hash<YourClass> {
        // specialization goes here
    };
}
like image 198
Yakov Galka Avatar answered Oct 16 '22 17:10

Yakov Galka