如何计算std::weak_ptr的哈希值以适配无序关联容器使用需求
std::weak_ptr 用于unordered_set的可行解决方案
std::unordered_set需要两个核心组件:哈希函数、相等谓词。对于std::weak_ptr来说,标准已经提供了std::owner_equal<std::weak_ptr<T>>作为相等谓词,可以正确判断两个weak_ptr是否指向同一个控制块,和你之前在std::set中使用的std::owner_less逻辑完全一致,缺少的只是和owner_equal匹配的哈希函数,以下是两个可直接落地的解决方案:
方案1:全平台通用无依赖方案(推荐)
不需要触碰任何标准库私有实现,完全符合C++标准要求,兼容性最好。
思路:将weak_ptr和预计算的哈希值封装为自定义类型,在从shared_ptr构造weak_ptr时,直接用标准库提供的std::hash<std::shared_ptr<T>>计算哈希值缓存下来,哈希函数直接返回缓存值即可。
#include <memory> #include <unordered_set> #include <functional> template <typename T> struct HashedWeakPtr { std::weak_ptr<T> ptr; size_t cached_hash; // 仅允许从有效shared_ptr构造 HashedWeakPtr(const std::shared_ptr<T>& s) : ptr(s), cached_hash(std::hash<std::shared_ptr<T>>{}(s)) {} bool operator==(const HashedWeakPtr<T>& other) const { return std::owner_equal<std::weak_ptr<T>>{}(ptr, other.ptr); } }; // 自定义哈希特化 namespace std { template <typename T> struct hash<HashedWeakPtr<T>> { size_t operator()(const HashedWeakPtr<T>& hw) const { return hw.cached_hash; } }; } // 使用示例,可直接替换为f14::F14ValueSet std::unordered_set<HashedWeakPtr<int>> g_set;
- 优点:100%标准兼容,跨所有编译器和标准库实现,行为和未来C++标准新增的
owner_hash()完全一致,和原有std::set的业务逻辑完全对齐,无缝迁移 - 缺点:64位系统下每个实例需要额外存储8字节的缓存哈希值,内存开销略有上升,绝大多数场景可忽略
方案2:零额外开销适配方案
如果无法接受额外的内存开销,可以通过条件编译适配不同标准库的私有实现,提前实现owner_hash接口,未来标准支持后可无缝切换。
#include <memory> #include <functional> template <typename T> size_t owner_hash(const std::weak_ptr<T>& wp) { #if __has_include(<version>) # include <version> #endif // 适配未来C++标准原生接口 #if __cpp_lib_weak_ptr_owner_hash >= 202403L return wp.owner_hash(); // 适配GCC libstdc++ #elif defined(__GLIBCXX__) return std::hash<void*>{}(wp._M_refcount._M_pi); // 适配Clang libc++ #elif defined(_LIBCPP_VERSION) return std::hash<void*>{}(wp.__cntrl_); // 适配MSVC标准库 #elif defined(_MSC_VER) return std::hash<void*>{}(wp._Getptr()); #else # error "当前编译环境未适配owner_hash实现,建议使用方案1" #endif } // 自定义哈希函数 template <typename T> struct WeakPtrHash { size_t operator()(const std::weak_ptr<T>& wp) const { return owner_hash(wp); } }; // 使用示例,可直接替换为f14::F14ValueSet std::unordered_set<std::weak_ptr<int>, WeakPtrHash<int>, std::owner_equal<std::weak_ptr<int>>> g_set;
- 优点:无额外内存开销,性能和原生标准支持一致
- 缺点:需要针对不同编译器做适配,新增编译环境时需要补充对应实现
注意:两种方案都必须配对使用
std::owner_equal<std::weak_ptr<T>>作为相等谓词,不能直接使用std::weak_ptr的默认operator==,否则会出现哈希和相等判断逻辑不匹配的问题。
内容的提问来源于stack exchange,提问作者Linas
相关产品推荐
相关产品推荐

