比较相等的键应该产生相同的哈希值。解耦散列函数和谓词,和同时使一个或两个异构,可能太容易出错。
最近的论文P0919r2 提出了以下示例:
std::hash<long>{}(-1L) == 18446744073709551615ULL
std::hash<double>{}(-1.0) == 11078049357879903929ULL
虽然-1L 和-1.0 比较相等,但某些异构哈希函数不符合所选相等比较逻辑,可能会产生不同的值。论文添加了支持异构查找的功能模板——
find、count、equal_range 和 contains -- 但在满足以下要求时使它们可用 [unord.req]/p17:
如果 qualified-id Hash::transparent_key_equal 是有效的并且表示一个类型 ([temp.deduct]),那么程序是非良构的:
-
qualified-id
Hash::transparent_key_equal::is_transparent 无效或不表示类型,或
-
Pred 与 equal_to<Key> 或 Hash::transparent_key_equal 是不同的类型。
成员函数模板 find、count、equal_range 和 contains 不应参与重载决议,除非 qualified-id Hash::transparent_key_equal 有效并表示类型([temp.deduct])。
在这种情况下,Hash::transparent_key_equal 会覆盖默认谓词 (std::equal_to<Key>) 并用于(透明)相等性检查,与 Hash 本身一起用于(透明)散列。
在这些条件下,可以使用以下透明函数对象来启用异构查找:
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; // or std::equal_to<>
std::size_t operator()(const std::string& s) const
{
return s.size();
}
std::size_t operator()(const char* s) const
{
return std::strlen(s);
}
};
string_equal 和 std::equal_to<> 都是透明的比较器,可以用作transparent_key_equal 的string_hash。
在散列函数类定义中具有此类型别名(或类型定义本身)明确它是一个有效的谓词,可以与特定的散列逻辑一起正常工作,而两者不能发散。这样一个无序集可以声明为:
std::unordered_set<std::string, string_hash> u;
或:
std::unordered_set<std::string, string_hash, string_hash::transparent_key_equal> u;
将使用string_hash 和string_equal。