ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

TBB concurrent_unordered_map 的非成员二元比较:operator== 与 operator!= 的规范语义与源码实现

TBB concurrent_unordered_map 的非成员二元比较:operator== 与 operator!= 的规范语义与源码实现 TBB concurrent_unordered_map 的非成员二元比较operator 与 operator! 的规范语义与源码实现【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold本文聚焦 Intel oneAPI TBB本仓库内嵌于 third-party/tbb 子目录中concurrent_unordered_map容器的非成员二元比较Non-member binary comparisons即规范文档 non_member_binary_comparisons.rst 所定义的operator与operator!。读完本文你将掌握无序容器相等性的判定条件、这两个运算符在 TBB 源码中的真实实现位置与调用链std::is_permutationsize()、C20 合成比较带来的实现差异以及并发环境下执行比较时的注意事项与验证方式。一、规范定义什么情况下两个容器“相等”规范文档给出的核心语义非常简洁——两个concurrent_unordered_map对象相等当且仅当以下两个条件同时成立两者包含的元素个数相等一个容器中的每个元素在另一个容器中也都存在。规范为这两个非成员运算符给出了如下函数签名与文档原文一致template typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator bool operator( const concurrent_unordered_mapKey, T, Hash, KeyEqual, Allocator lhs, const concurrent_unordered_mapKey, T, Hash, KeyEqual, Allocator rhs );返回值lhs与rhs相等返回true否则返回false。template typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator bool operator!( const concurrent_unordered_mapKey, T, Hash, KeyEqual, Allocator lhs, const concurrent_unordered_mapKey, T, Hash, KeyEqual, Allocator rhs );operator!与!(lhs rhs)等价不相等返回true否则返回false。这里有两点值得注意相等性由 key 与 value 共同决定。concurrent_unordered_map的value_type是std::pairconst Key, T见 concurrent_unordered_map.h 中concurrent_unordered_map_traits的定义因此“元素相同”意味着键值对整体相等——即使键相同只要映射值不同容器也不相等。顺序无关。无序容器的迭代顺序由哈希桶分布决定两个完全同源的容器迭代顺序也可能不同规范用“每个元素也都存在”这一集合式语义而非逐位置比较正体现了“unordered”的容器特性。两个操作数必须是同一模板参数组合相同的Key、T、Hash、KeyEqual、Allocator的concurrent_unordered_map。二、源码实现运算符定义在共享基类上规范只描述了concurrent_unordered_map的接口但从源码结构看这两个运算符实际上定义在所有无序并发容器的公共基类concurrent_unordered_base上。这意味着concurrent_unordered_multimap复用的是同一套实现——这也解释了为什么 concurrent_unordered_map.h 中concurrent_unordered_map与concurrent_unordered_multimap两个类体内都没有成员operator/operator!真正的定义位于 _concurrent_unordered_base.htemplate typename Traits bool operator( const concurrent_unordered_baseTraits lhs, const concurrent_unordered_baseTraits rhs ) { if (lhs rhs) { return true; } if (lhs.size() ! rhs.size()) { return false; } #if _MSC_VER // Passing unchecked iterators to std::permutation with 3 parameters // causes compiler warnings. // The workaround is to use overload with 4 parameters, which is // available since C14 - minimally supported version on MSVC return std::is_permutation(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); #else return std::is_permutation(lhs.begin(), lhs.end(), rhs.begin()); #endif } #if !__TBB_CPP20_COMPARISONS_PRESENT template typename Traits bool operator!( const concurrent_unordered_baseTraits lhs, const concurrent_unordered_baseTraits rhs ) { return !(lhs rhs); } #endif这段实现与规范语义逐条对应可以拆解为三个步骤自比较短路lhs rhs时直接返回true。规范中“元素个数相等 元素互含”对同一对象天然成立此判断只是零成本优化。先比大小lhs.size() ! rhs.size()时立即返回false。这正是规范第 1 条“包含相等数量的元素”——先做廉价的计数比较避免进入昂贵的逐元素扫描。再判“元素互含”std::is_permutation(lhs.begin(), lhs.end(), rhs.begin())判定rhs是否为lhs元素序列的某个排列即rhs中的每个元素都能按value_type的operator在lhs中找到匹配反之亦然。这恰好实现了规范第 2 条并且天然不受迭代顺序影响。细节说明is_permutation的参数形式在 MSVC 上使用四参数重载显式传入第四组迭代器是为了规避对“unchecked”迭代器使用三参数重载时的编译器告警注释中说明四参数重载自 C14 起可用而 C14 是 MSVC 的最低支持版本。两种重载语义一致。size()是并发感知的。基类中size()的实现是my_size.load(std::memory_order_relaxed)见 _concurrent_unordered_base.h即从一个原子计数器松弛读取。TBB 规范在 Size and capacity 章节size_and_capacity.rst中明确指出存在并发插入时size()的结果可能与容器实际状态不同。推论若比较执行的同时其他线程正在并发insertoperator的结果反映的是比较开始时的快照状态而非“最终一致”的状态——这与 TBB 其他接口的并发语义一脉相承编写测试或断言时应避免在并发写入进行中直接比较容器。对concurrent_unordered_multimap的适用性基类通过Traits模板参数区分 mapallow_multimapping false与 multimaptrue见 concurrent_unordered_map.h 的concurrent_unordered_map_traits但比较运算符对两者一视同仁。std::is_permutation对重复元素按重数匹配因此 multimap 中“同一键两个不同值”与“同一键重复两次”会被区分开与std::unordered_multimap的相等性语义保持一致。三、C20 合成比较operator!为何有条件编译注意源码中operator!被#if !__TBB_CPP20_COMPARISONS_PRESENT包裹而operator始终显式定义。这个宏在 _config.h 中判定#if defined(__cpp_impl_three_way_comparison) defined(__cpp_lib_three_way_comparison) #define __TBB_CPP20_COMPARISONS_PRESENT ((__cpp_impl_three_way_comparison 201907L) (__cpp_lib_three_way_comparison 201907L)) #else #define __TBB_CPP20_COMPARISONS_PRESENT 0 #endif其含义是C17 及更早编译器不会为operator自动合成operator!所以必须手写return !(lhs rhs);这正是规范中“operator!与!(lhs rhs)等价”的落地方式。C20 起检测到__cpp_impl_three_way_comparison与__cpp_lib_three_way_comparison特性宏编译器会为显式定义的operator自动合成operator!语义即!(a b)。若再手写一份反而可能与合成版本产生歧义或重复定义因此源码选择在 C20 环境下省略手写版本。同样的模式也出现在 TBB 的其他容器中例如 concurrent_hash_map.h 与 concurrent_queue.h 中的operator!都受同一宏保护属于 TBB 全库统一的实现约定。四、使用示例与验证途径典型用法#include oneapi/tbb/concurrent_unordered_map.h tbb::concurrent_unordered_mapint, int a; a.insert({1, 10}); a.insert({2, 20}); tbb::concurrent_unordered_mapint, int b a; // 拷贝构造 assert(a b); // 元素相同相等 assert(!(a ! b)); // 与 !(a b) 等价 a[3] 30; // operator[] 不存在键时会插入见 concurrent_unordered_map.h assert(a ! b); // 元素个数/内容不同 assert(!(a b));要点复述键值对整体参与比较{1, 10}与{1, 11}被视为不同元素因此即使键集合相同、仅映射值不同a b也为false。迭代顺序不影响结果实现走std::is_permutation桶分布不同导致的顺序差异不会被误判为不相等。相关测试与规范脉络该文档位于 TBB 规范concurrent_unordered_map类参考手册的同级章节目录 concurrent_unordered_map_cls/ 中与构造/复制construction_destruction_copying.rst、查找lookup.rst、观察器observers.rst、非成员swapnon_member_swap.rst等章节并列共同构成该容器的完整 API 参考。功能测试入口在 test_concurrent_unordered_map.cpp测试用tbb::concurrent_unordered_mapint, int, std::hashint, std::equal_toint, ...等类型组合含degenerate_hash退化哈希场景覆盖 map 与 multimap 的行为同目录的 conformance_concurrent_unordered_map.cpp 则属于 C 标准一致性conformance测试集。非成员swap与比较运算符一样是基类模板参数的函数参见 non_member_swap.rst在 concurrent_unordered_map.h 中通过lhs.swap(rhs)转发到成员swap。五、小结项目规范描述源码实现要点operator元素个数相等且元素互含地址相等短路 →size()原子读取比对 →std::is_permutation判定元素集合相等定义于concurrent_unordered_baseoperator!与!(lhs rhs)等价C20 下由编译器合成C17 下显式定义受__TBB_CPP20_COMPARISONS_PRESENT宏保护顺序敏感性无序容器顺序无关is_permutation天然容忍迭代顺序差异并发语义规范未单独说明size()为松弛原子读取并发插入进行中时比较结果可能滞后于实际状态对使用者而言这条 API 的规范语义、实际实现与验证路径在本仓库中形成了完整闭环规范文档non_member_binary_comparisons.rst定义“相等”的判定条件基类头文件_concurrent_unordered_base.h给出与之一一对应的三段式实现配置头文件_config.h解释了 C20 下的条件编译差异而test/tbb与test/conformance下的测试提供了行为验证的入口。【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表