ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

OneTBB 中 concurrent_unordered_set 的类模板参数推导(CTAD)指南:从规范到源码实现

OneTBB 中 concurrent_unordered_set 的类模板参数推导(CTAD)指南:从规范到源码实现 OneTBB 中 concurrent_unordered_set 的类模板参数推导CTAD指南从规范到源码实现【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold本文以 Intel oneTBBoneAPI Threading Building Blocks作为 mold 仓库的第三方依赖位于third-party/tbb容器规范中的 Deduction Guides 章节为核心讲解concurrent_unordered_set在 C17 类模板参数推导Class Template Argument DeductionCTAD下的完整语义6 条显式推导指南、4 条参与重载决议的前提条件、iterator_value_t别名类型以及它们在头文件中的真实 SFINAE 实现。读完后你可以直接使用auto或无模板实参的写法构造并发无序集合并理解编译器如何替你推导出Key、Hash、KeyEqual、Allocator四个模板参数。一、为什么 concurrent_unordered_set 需要推导指南concurrent_unordered_set是一个四参数模板容器其类定义见 concurrent_unordered_set.h为template typename Key, typename Hash std::hashKey, typename KeyEqual std::equal_toKey, typename Allocator tbb::tbb_allocatorKey class concurrent_unordered_set : public concurrent_unordered_baseconcurrent_unordered_set_traitsKey, Hash, KeyEqual, Allocator, false从源码结构看全部构造逻辑桶数、区间初始化、std::initializer_list初始化等都下沉到基类concurrent_unordered_base中派生类仅通过using base_type::base_type;引入这些构造函数见 concurrent_unordered_set.h。这正是 CTAD 的难点当用户写concurrent_unordered_set s1(v.begin(), v.end());而不给出任何模板实参时编译器并不能像普通类模板那样从构造函数形参直接匹配到类模板参数——因为这里的参数是InputIterator迭代器而容器真正需要的第一个参数是Key元素类型。因此规范为区间构造、初始化列表构造提供了显式推导指南explicit deduction guides把迭代器的value_type显式地翻译成Key。规范文档指出拷贝构造和移动构造包括带显式allocator_type参数的形式提供编译器隐式生成的推导指南此外还提供下面 6 条显式指南。二、显式推导指南全集继承自规范文档规范原文给出的 6 条显式推导指南如下完整保留供参考template typename InputIterator, typename Hash std::hashiterator_value_tInputIterator, typename KeyEqual std::equal_toiterator_value_tInputIterator, typename Allocator tbb::tbb_allocatoriterator_value_tInputIterator concurrent_unordered_set( InputIterator, InputIterator, set_size_type {}, Hash Hash(), KeyEqual KeyEqual(), Allocator Allocator() ) - concurrent_unordered_setiterator_value_tInputIterator, Hash, KeyEqual, Allocator; template typename InputIterator, typename Allocator concurrent_unordered_set( InputIterator, InputIterator, set_size_type, Allocator ) - concurrent_unordered_setiterator_value_tInputIterator, std::hashiterator_value_tInputIterator, std::equal_toiterator_value_tInputIterator, Allocator; template typename T, typename Hash std::hashT, typename KeyEqual std::equal_toT, typename Allocator tbb::tbb_allocatorT concurrent_unordered_set( std::initializer_listT, set_size_type {}, Hash Hash(), KeyEqual KeyEqual(), Allocator Allocator() ) - concurrent_unordered_setT, Hash, KeyEqual, Allocator; template typename T, typename Allocator concurrent_unordered_set( std::initializer_listT, set_size_type, Allocator ) - concurrent_unordered_setT, std::hashT, std::equal_toT, Allocator; template typename T, typename Allocator concurrent_unordered_set( std::initializer_listT, Allocator ) - concurrent_unordered_setT, std::hashT, std::equal_toT, Allocator; template typename T, typename Hash, typename Allocator concurrent_unordered_set( std::initializer_listT, set_size_type, Hash, Allocator ) - concurrent_unordered_setT, Hash, std::equal_toT, Allocator;2.1 两个关键类型的含义set_size_type指被推导出的那个concurrent_unordered_set的size_type成员类型。它在推导指南中仅作为参数类型占位即一个 size_t 大小的桶数参数推导结果本身不依赖它。iterator_value_t用于从迭代器反推元素类型的别名规范中定义为template typename InputIterator using iterator_value_t typename std::iterator_traitsInputIterator::value_type;这个别名在仓库源码中原样存在见 _template_helpers.h。2.2 推导结果速查表构造调用形态推导出的模板参数(It, It)或(It, It, n, hash, eq, alloc)Key iterator_value_tItHash/KeyEqual/Allocator取对应默认值或传入值(It, It, n, alloc)Key iterator_value_tItHash std::hashKeyKeyEqual std::equal_toKeyAllocator alloc(std::initializer_listT, n, hash, eq, alloc)Key T其余取传入值或默认值(std::initializer_listT, n, alloc)/(std::initializer_listT, alloc)Key THash std::hashTKeyEqual std::equal_toTAllocator alloc(std::initializer_listT, n, hash, alloc)Key THash取传入值KeyEqual固定为std::equal_toTAllocator alloc三、参与重载决议的 4 条前提条件规范明确规定上述推导指南仅在下述要求全部满足时才参与重载决议InputIterator满足 ISO C 标准 [input.iterators] 一节中定义的InputIterator要求Allocator满足 [allocator.requirements] 一节中定义的Allocator要求Hash类型不满足Allocator要求KeyEqual类型不满足Allocator要求。条件 3、4 的动机很实际Hash、KeyEqual、Allocator三个位置参数在值类型上没有本质区别都是传一个对象如果用户的Hash类型恰好也长得像一个 allocator例如自定义哈希器带有allocate/deallocate成员推导会产生歧义。用是不是 allocator作为判据可以在 SFINAE 层面消歧。3.1 源码中的 SFINAE 落地这 4 条条件在头文件里逐条落实为std::enable_if约束位于 concurrent_unordered_set.h#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT template typename It, typename Hash std::hashiterator_value_tIt, typename KeyEq std::equal_toiterator_value_tIt, typename Alloc tbb::tbb_allocatoriterator_value_tIt, typename std::enable_if_tis_input_iterator_vIt, // 条件 1 typename std::enable_if_tis_allocator_vAlloc, // 条件 2 typename std::enable_if_t!is_allocator_vHash, // 条件 3 typename std::enable_if_t!is_allocator_vKeyEq, // 条件 4 typename std::enable_if_t!std::is_integral_vHash concurrent_unordered_set( It, It, std::size_t {}, Hash Hash(), KeyEq KeyEq(), Alloc Alloc() ) - concurrent_unordered_setiterator_value_tIt, Hash, KeyEq, Alloc;两个判据 trait 的定义is_allocator要求类型同时具备value_type成员、可调用allocate、可调用deallocate见 _template_helpers.htemplate typename T using is_allocator supportsT, alloc_value_type, has_allocate, has_deallocate;is_input_iterator通过std::iterator_traits的iterator_category是否派生自std::input_iterator_tag判断见 _containers_helpers.h。两处值得注意的实现细节set_size_type在实现中直接写成了std::size_t与规范中被推导容器的size_type等价但更直接源码比规范多出一条指南。除了规范列出的 6 条实现中还额外提供了(It, It, std::size_t, Hash, Alloc)这条指南即桶数 哈希器 分配器、不带比较器的区间构造形式见 concurrent_unordered_set.h每条指南还附带了!std::is_integral_vHash约束防止把整数字面量例如误写的桶数推导成Hash模板参数。四、隐式推导指南拷贝与移动构造规范说明拷贝构造和移动构造包括带allocator_type参数的形式提供隐式生成的推导指南。源码中为此做了显式铺垫——在 concurrent_unordered_set.h 中可以看到专门注释// Required for implicit deduction guides的默认可用构造函数// Required for implicit deduction guides concurrent_unordered_set() default; concurrent_unordered_set( const concurrent_unordered_set ) default; concurrent_unordered_set( const concurrent_unordered_set other, const allocator_type alloc ) : base_type(other, alloc) {} concurrent_unordered_set( concurrent_unordered_set ) default; concurrent_unordered_set( concurrent_unordered_set other, const allocator_type alloc ) : base_type(std::move(other), alloc) {}这使得下面写法合法s2被推导为与s1完全相同的类型concurrent_unordered_set s2(s1); concurrent_unordered_set s3(std::move(s1)); concurrent_unordered_set s4(s1, my_alloc);源码中还有一处针对特定编译器的显式补丁在 Apple LLVM 10.0.0 下隐式推导指南的生成存在缺陷源码注释归因于Traits模板参数末尾是布尔值因此手动补写了一条拷贝/移动分配器形式的显式指南见 concurrent_unordered_set.h。这提示了阅读该仓库源码时的一点背景这些#if __APPLE__ __TBB_CLANG_VERSION 100000块是编译器兼容性 workaround而非公共 API。五、编译期可用性哪些编译器能启用 CTAD所有显式推导指南都被包裹在#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT ... #endif中。该宏按编译器逐一判定见 _config.h编译器判定条件Intel (icx/icpx)__INTEL_COMPILER 2021且__TBB_LANG 201703LTODO 注释表明旧版 ICX 的 CTAD 支持曾有问题Clang__cpp_deduction_guides 201611LGCC__cpp_deduction_guides 201606LMSVC_MSC_VER 1914VS 2017 15.7且__TBB_LANG 201703L其他__TBB_LANG 201703L也就是说使用推导指南的前提是 C17或更新标准且编译器对 CTAD 的支持达到相应阈值不满足时该头文件静退回到必须手写模板实参的模式concurrent_unordered_setint s(v.begin(), v.end());之类的显式写法仍然可用。六、实战示例规范文档给出的示例完整继承#include oneapi/tbb/concurrent_unordered_set.h #include vector #include functional struct CustomHasher {...}; int main() { std::vectorint v; // Deduces s1 as concurrent_unordered_setint oneapi::tbb::concurrent_unordered_set s1(v.begin(), v.end()); // Deduces s2 as concurrent_unordered_setint, CustomHasher; oneapi::tbb::concurrent_unordered_set s2(v.begin(), v.end(), CustomHasher{}); }结合第二节的速查表可以进一步覆盖初始化列表与分配器组合#include oneapi/tbb/concurrent_unordered_set.h #include oneapi/tbb/tbb_allocator.h // 推导为 concurrent_unordered_setstd::string默认 std::hash/std::equal_to/tbb_allocator oneapi::tbb::concurrent_unordered_set s1{ a, b, c }; // 指定桶数 自定义分配器Keystd::string, Hashstd::hash, KeyEqualstd::equal_to, AllocMyAlloc oneapi::tbb::concurrent_unordered_set s2{ a, b /*, n*/, my_alloc }; // 区间构造 只给分配器 std::vectorint v{1, 2, 3}; oneapi::tbb::concurrent_unordered_set s3(v.begin(), v.end(), 64, my_alloc);这些推导行为由仓库的符合性测试覆盖测试入口为 conformance_concurrent_unordered_set.cpp可在third-party/tbb/test目录下按测试框架约定运行测试需要构建 oneTBB 库本身属于 TBB 子项目的构建流程与 mold 主构建相互独立。七、相关文档与延伸阅读本文依据的规范章节deduction_guides.rst构造函数的完整签名与语义推导指南所对应的真实构造函数都在这里construction_destruction_copying.rstconcurrent_unordered_set类规范索引页含 iterators、lookup、observers 等同级章节concurrent_unordered_set_cls.rst同构推导指南在其他容器上的复用concurrent_unordered_multiset同一头文件内、concurrent_queue、concurrent_priority_queue、concurrent_hash_map等均按同一套iterator_value_tis_allocatorenable_if模式实现如 concurrent_priority_queue.h 中的指南块可作为理解 oneTBB 容器 CTAD 设计的一致性参照。要点回顾oneTBB 的concurrent_unordered_set通过 6 条显式推导指南 拷贝/移动构造的隐式指南让auto风格的无实参构造成为可能推导核心是把迭代器的value_type映射为Key并把默认Hash/KeyEqual/Allocator与类模板默认实参保持一致4 条参与条件则以is_input_iterator/is_allocator两个 trait 在 SFINAE 层面落地避免哈希器、比较器与分配器之间的参数位置歧义。【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表