Does the standard guarantee that the standard library std::hash<> specializations produce identical hashes for values that compare equal, even if they are of different types? For example, would the following two always produce the same value?
std::hash<std::int32_t>{}(1);
std::hash<std::int64_t>{}(1);
The context for my question is: I am using a hash class that allows for transparent lookup for isomorphic tuple types, for example, given an unordered_map like so:
std::unordered_map<std::tuple<int, string, double>, string>
I can do a lookup on it with a std::tuple<int, string&, double>. The implementation just uses std::hash on each element and combines the hashes together. In this case, if I am given a std::tuple<int64_t, string, double>, I'm wondering if I can assume the hash of this would produce the same output if each tuple element compares equal.
From cppreference.com:
The actual hash functions are implementation-dependent and are not required to fulfill any other quality criteria except those specified above.
The criteria referenced by this quote are mostly technical (e.g. default constructible), plus the traditional hash requirement: if two values of the type Key compare equal, then std::hash<Key>{}() maps those input values to the same hash value. In particular, there is no requirement imposed for input values of different types.
In general, it would be difficult (impossible) to require equality of hashes for equal values of different types. For user-defined types, one difficulty is that there might have not been a definition of equality when the hash function was defined. (Should introducing such an operator== invalidate existing hashes? No.) However, you're not using user-defined types, so this is just background as to why there is no universal requirement.
For strings, there is a requirement that std::hash<std::string_view> produce the same hash as std::hash<std::string> when the view and string are equal. So this is good for your use-case. (But be aware that this does not hold for null-terminated strings; std::hash<char*> hashes based upon the pointer value, not the pointed-to data.) For the standard arithmetic types, such a requirement would be technically possible, but it has not been imposed. Fortunately, though, cppreference goes on to note
Notably, some implementations use trivial (identity) hash functions which map an integer to itself.
If this is the case for your implementation, your plan would work (for as long as this remains true for your implementation).
On the other hand, why not apply a static_cast<int> before hashing to be sure?
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