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..
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
};
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With