C++17如何为多个继承自Base的类统一特化std::hash?
实现方案
方案1:偏特化std::hash批量适配所有派生类
利用C++17的类型萃取特性和SFINAE规则,仅对公开继承自Base的派生类生效:
#include <type_traits> // 注意必须在std命名空间内定义该特化 namespace std { template <typename T> struct hash<T, std::enable_if_t< // 仅匹配可公开转换为Base的派生类,排除Base自身 std::is_convertible_v<const T*, const Base*> && !std::is_same_v<std::decay_t<T>, Base> >> { size_t operator()(const T& obj) const { return obj.GetHash(); } }; }
定义完成后,所有公开继承Base的派生类都可以直接作为std::unordered_map/std::unordered_set的键使用,不需要再手动传入HashKey模板参数:
// 直接使用,无需额外传哈希函数 std::unordered_map<MyDerived, int> my_map; std::unordered_set<MyDerived> my_set;
方案2:无需修改std命名空间的简化方案
如果你不想往std命名空间添加自定义特化(符合更严格的编码规范要求),可以定义模板别名简化容器声明:
template <typename Key, typename Value> using BaseDerivedUnorderedMap = std::unordered_map<Key, Value, HashKey>; template <typename T> using BaseDerivedUnorderedSet = std::unordered_set<T, HashKey>;
使用方式:
BaseDerivedUnorderedMap<MyDerived, int> my_map; BaseDerivedUnorderedSet<MyDerived> my_set;
注意事项
- 所有派生类的
GetHash()实现必须满足哈希函数的约束:相等的对象必须返回完全相同的哈希值,不同对象哈希冲突的概率尽可能低。 - 若使用方案1的
std::hash特化,必须保证该特化在所有使用派生类作为unordered容器键的代码之前可见,否则会触发未定义行为。 - unordered容器同时依赖哈希函数和相等性判断逻辑,你需要确保派生类的
operator==实现和GetHash()逻辑匹配:若两个对象==返回true,其GetHash()返回值必须相等。
内容的提问来源于stack exchange,提问作者Pavel Lyubkin
相关产品推荐
相关产品推荐

