Keys that compare the same must have the same hash value. Decoupling the hash function and the predicate and at the same time creating one or both heterogeneous ones may be too error prone.
A recent article, P0919r2 , provides the following example:
std::hash<long>{}(-1L) == 18446744073709551615ULL std::hash<double>{}(-1.0) == 11078049357879903929ULL
Although -1L and -1.0 compared equal, some heterogeneous hash function that does not match the selected equality comparison logic can produce different values. Heterogeneous function templates with search support have been added to the document - find , count , equal_range and contains - but makes them available when the [unord.req] / p17 requirements are met below:
If valid-id Hash::transparent_key_equal valid and indicates the type ([temp.deduct]), then the program is poorly formed if:
- qualified-id
Hash::transparent_key_equal::is_transparent invalid or does not indicate a type, or Pred is a different type than equal_to<Key> or Hash::transparent_key_equal .
The member templates of the find , count , equal_range and contains functions should not participate in overload resolution, unless the valid Hash::transparent_key_equal identifier is valid and indicates the type ([temp.deduct]).
In this case, Hash::transparent_key_equal overwrites the default predicate ( std::equal_to<Key> ) and is used to (transparent) check equality with Hash itself for (transparent) hashing.
Under these conditions, the following transparent functional objects can be used to provide a heterogeneous search:
struct string_equal { using is_transparent = void; bool operator()(const std::string& l, const std::string& r) const { return l.compare(r) == 0; } bool operator()(const std::string& l, const char* r) const { return l.compare(r) == 0; } bool operator()(const char* l, const std::string& r) const { return r.compare(l) == 0; } }; struct string_hash { using transparent_key_equal = string_equal;
Both string_equal and std::equal_to<> are transparent comparators and can be used as transparent_key_equal for string_hash .
The presence of this type alias (or the type definition itself) in the definition of the hash function class makes it clear that it is a valid predicate that works perfectly with this particular hashing logic, and they cannot diverge. Such an unordered set can be declared as:
std::unordered_set<std::string, string_hash> u;
or:
std::unordered_set<std::string, string_hash, string_hash::transparent_key_equal> u;
Or it will use string_hash and string_equal .