mold 内置 TBB 详解:concurrent_unordered_map 的 C++17 推导指引(Deduction Guides)

发布时间:2026/9/14 8:14:14
mold 内置 TBB 详解:concurrent_unordered_map 的 C++17 推导指引(Deduction Guides) mold 内置 TBB 详解concurrent_unordered_map 的 C17 推导指引Deduction Guides【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold本文基于 mold 仓库中内置的 TBBoneTBB第三方组件围绕容器规范文档 deduction_guides.rst 展开系统讲解concurrent_unordered_map构造函数对 C17 类模板参数推导CTAD的支持方式包括隐式推导指引、七组显式推导指引的完整签名、类型别名定义与参与重载决议的前提条件并结合 concurrent_unordered_map.h 的实际源码印证每条推导指引的真实实现与 SFINAE 约束。读完后你可以直接在不写模板参数的情况下正确推导concurrent_unordered_map的完整类型并理解推导失败的边界条件。背景concurrent_unordered_map 的模板参数与推导动机concurrent_unordered_map定义于 concurrent_unordered_map.h其模板参数共有 5 个template typename Key, typename T, typename Hash std::hashKey, typename KeyEqual std::equal_toKey, typename Allocator tbb::tbb_allocatorstd::pairconst Key, T class concurrent_unordered_map : public concurrent_unordered_baseconcurrent_unordered_map_traitsKey, T, Hash, KeyEqual, Allocator, false;完整写法concurrent_unordered_mapint, float之外的参数大多有默认值但在实际使用时经常需要指定自定义Hash或Allocator完整写出所有参数会很冗长。C17 引入的类模板参数推导class template argument deductionCTAD允许编译器从构造函数的实参类型反推出类模板参数这正是该规范文档的主题concurrent_unordered_map的构造函数在可能的情况下支持 CTAD。从源码结构看整个容器通过继承concurrent_unordered_base实现位于 detail/_concurrent_unordered_base.h来复用范围构造、初始化列表构造等逻辑例如范围构造器_concurrent_unordered_base.htemplate typename InputIterator concurrent_unordered_base( InputIterator first, InputIterator last, size_type bucket_count initial_bucket_count, const hasher hash hasher(), const key_equal equal key_equal(), const allocator_type alloc allocator_type() ) : concurrent_unordered_base(bucket_count, hash, equal, alloc) { insert(first, last); }推导指引推导出的类型最终都会落到这一组构造函数上。文档中反复出现的map_size_type参数指的就是被推导出的concurrent_unordered_map的size_type成员类型。隐式推导指引拷贝与移动构造函数文档第一要点拷贝构造函数和移动构造函数——包括显式带allocator_type参数的那些——提供由编译器隐式生成的推导指引。这一点在源码中有明确标注concurrent_unordered_map.h// Required for implicit deduction guides concurrent_unordered_map() default; concurrent_unordered_map( const concurrent_unordered_map ) default; concurrent_unordered_map( const concurrent_unordered_map other, const allocator_type alloc ) : base_type(other, alloc) {} concurrent_unordered_map( concurrent_unordered_map ) default; concurrent_unordered_map( concurrent_unordered_map other, const allocator_type alloc ) : base_type(std::move(other), alloc) {}也就是说concurrent_unordered_map m2(m1);、concurrent_unordered_map m3(std::move(m1), alloc);这类写法都能借助隐式推导指引得到与实参相同的完整模板参数无需手写类型。注释// Required for implicit deduction guides说明这些默认化的特殊成员函数是刻意保留的目的就是让编译器能据此生成隐式推导指引。显式推导指引七组完整签名在隐式指引之外文档列出了 7 条显式推导指引覆盖“迭代器范围构造”和“初始化列表构造”两大入口按参数个数1 个/2 个/5 个参数细分。以下完整保留文档中的签名map_size_type为被推导类型的size_type1. 迭代器范围 全部可选参数默认 Hash / KeyEqual / Allocatortemplate typename InputIterator, typename Hash std::hashiterator_key_tInputIterator, typename KeyEqual std::equal_toiterator_key_tInputIterator, typename Allocator tbb::tbb_allocatoriterator_alloc_value_tInputIterator concurrent_unordered_map( InputIterator, InputIterator, map_size_type {}, Hash Hash(), KeyEqual KeyEqual(), Allocator Allocator() ) - concurrent_unordered_mapiterator_key_tInputIterator, iterator_mapped_tInputIterator, Hash, KeyEqual, Allocator;这是最常用的一条仅给两个迭代器即可从value_type::first_type去 const与value_type::second_type推导出Key和THash、KeyEqual、Allocator全部取默认值。2. 迭代器范围 指定 Hash 和 AllocatorKeyEqual 固定为 equal_totemplate typename InputIterator, typename Hash, typename Allocator concurrent_unordered_map( InputIterator, InputIterator, map_size_type, Hash, Allocator ) - concurrent_unordered_mapiterator_key_tInputIterator, iterator_mapped_tInputIterator, Hash, std::equal_toiterator_key_tInputIterator, Allocator;3. 迭代器范围 只指定 Allocatortemplate typename InputIterator, typename Allocator concurrent_unordered_map( InputIterator, InputIterator, map_size_type, Allocator ) - concurrent_unordered_mapiterator_key_tInputIterator, iterator_mapped_tInputIterator, std::hashiterator_key_tInputIterator, std::equal_toiterator_key_tInputIterator, Allocator;4. 初始化列表 全部可选参数Key 去 consttemplate typename Key, typename T, typename Hash std::hashstd::remove_const_tKey, typename KeyEqual std::equal_tostd::remove_const_tKey, typename Allocator tbb::tbb_allocatorstd::pairconst Key, T concurrent_unordered_map( std::initializer_liststd::pairKey, T, map_size_type {}, Hash Hash(), KeyEqual KeyEqual(), Allocator Allocator() ) - concurrent_unordered_mapstd::remove_const_tKey, T, Hash, KeyEqual, Allocator;注意初始化列表形式中 Key 的推导是std::remove_const_tKey因为std::pairconst Key, T中首元素天然带 const推导时要把这层 const 剥掉保证与类模板参数Key的语义一致。5. 初始化列表 map_size_type Allocatortemplate typename Key, typename T, typename Allocator concurrent_unordered_map( std::initializer_liststd::pairKey, T, map_size_type, Allocator ) - concurrent_unordered_mapstd::remove_const_tKey, T, std::hashstd::remove_const_tKey, std::equal_tostd::remove_const_tKey, Allocator;6. 初始化列表 仅 Allocatortemplate typename Key, typename T, typename Allocator concurrent_unordered_map( std::initializer_liststd::pairKey, T, Allocator ) - concurrent_unordered_mapstd::remove_const_tKey, T, std::hashstd::remove_const_tKey, std::equal_tostd::remove_const_tKey, Allocator;7. 初始化列表 map_size_type Hash Allocatortemplate typename Key, typename T, typename Hash, typename Allocator concurrent_unordered_map( std::initializer_liststd::pairKey, T, map_size_type, Hash, Allocator ) - concurrent_unordered_mapstd::remove_const_tKey, T, Hash, std::equal_tostd::remove_const_tKey, Allocator;类型别名iterator_key_t、iterator_mapped_t、iterator_alloc_value_t文档给出的三个辅助类型别名为template typename InputIterator using iterator_key_t std::remove_const_ttypename std::iterator_traitsInputIterator::value_type::first_type; template typename InputIterator using iterator_mapped_t typename std::iterator_traitsInputIterator::value_type::second_type; template typename InputIterator using iterator_alloc_value_t std::pairstd::add_const_titerator_key_tInputIterator, iterator_mapped_tInputIterator;即Key 取迭代器value_type即std::pair的first_type并去除 constmapped 取second_type分配器所管理的值类型为“const Key mapped”组成的 pair与类默认分配器tbb::tbb_allocatorstd::pairconst Key, T保持一致。在仓库源码中这三个别名实际定义在 detail/_template_helpers.h其中第三个别名的名称是iterator_alloc_pair_t规范文档中写作iterator_alloc_value_t二者语义完全相同template typename Iterator using iterator_value_t typename std::iterator_traitsIterator::value_type; template typename Iterator using iterator_key_t typename std::remove_consttypename iterator_value_tIterator::first_type::type; template typename Iterator using iterator_mapped_t typename iterator_value_tIterator::second_type; template typename Iterator using iterator_alloc_pair_t std::pairtypename std::add_constiterator_key_tIterator::type, iterator_mapped_tIterator;这一实现细节也解释了为什么“给迭代器范围指定了 Allocator”与“使用默认 Allocator”会走不同的推导指引默认分配器必须精确匹配推导出的std::pairconst Key, T值类型而用户自定义分配器的值类型不受此约束因此源码将二者拆成独立指引以便 SFINAE 区分。参与重载决议的前提条件文档明确了这些推导指引参与重载决议的四项要求InputIterator满足 ISO C 标准 [input.iterators] 一节描述的 InputIterator 要求Allocator满足 [allocator.requirements] 一节描述的 Allocator 要求Hash类型不满足Allocator 要求KeyEqual类型不满足Allocator 要求。后两条要求的目的从源码结构看是为了消除歧义Hash/KeyEqual参数与Allocator参数在推导指引中的位置相近如果某个自定义类型既像 Hasher 又像 Allocator两条指引将同时可推导。源码用 SFINAE 把这些约束落实在每条推导指引上例如 concurrent_unordered_map.htemplate typename It, typename Hash std::hashiterator_key_tIt, typename KeyEq std::equal_toiterator_key_tIt, typename Alloc tbb::tbb_allocatoriterator_alloc_pair_tIt, typename std::enable_if_tis_input_iterator_vIt, typename std::enable_if_tis_allocator_vAlloc, typename std::enable_if_t!is_allocator_vHash, typename std::enable_if_t!is_allocator_vKeyEq, typename std::enable_if_t!std::is_integral_vHash concurrent_unordered_map( It, It, std::size_t {}, Hash Hash(), KeyEq KeyEq(), Alloc Alloc() ) - concurrent_unordered_mapiterator_key_tIt, iterator_mapped_tIt, Hash, KeyEq, Alloc;其中is_input_iterator_v与is_allocator_v是特性探测trait detection定义于 detail/_template_helpers.his_allocator依次探测类型是否具备value_type、可调用allocate、可调用deallocate三项都满足才判定为 Allocator。此外源码比规范文档多了一层保险——!std::is_integral_vHash如果第三个实参是整数字面量例如误把桶数写在 Hash 的位置推导会因 SFINAE 失败而被剔除从而把错误导向更清晰的诊断。文档中map_size_type {}这一可省参数在源码中对应std::size_t {}即桶数bucket_count传入后会传给concurrent_unordered_base的构造器并执行round_up_to_power_of_two见 _concurrent_unordered_base.h。所有显式推导指引都被#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT宏包裹concurrent_unordered_map.h保证在不支持 C17 推导指引的编译器下整段静默剔除容器本身仍可显式写模板参数使用。实战示例不写类型参数构造容器文档给出的官方示例如下可直接复制运行需链接 TBB 库#include oneapi/tbb/concurrent_unordered_map.h #include vector #include functional struct CustomHasher {...}; int main() { std::vectorstd::pairint, float v; // 推导 m1 为 concurrent_unordered_mapint, float oneapi::tbb::concurrent_unordered_map m1(v.begin(), v.end()); // 推导 m2 为 concurrent_unordered_mapint, float, CustomHasher; oneapi::tbb::concurrent_unordered_map m2(v.begin(), v.end(), CustomHasher{}); }逐条对应推导过程m1命中第 1 条推导指引迭代器value_type为std::pairint, float故 Key 推导为int、mapped 推导为floatHash/KeyEqual/Allocator全部取默认模板参数m2传入自定义CustomHasher{}后第三个实参不再是std::size_t编译器在可推导的指引中选择 Hash 参数槽位能接住CustomHasher的那一条前提是CustomHasher不满足 Allocator 要求且非整型最终得到concurrent_unordered_mapint, float, CustomHasher初始化列表形式同理例如concurrent_unordered_map m{{1, 1.0f}, {2, 2.0f}};会按第 4 条指引推导为concurrent_unordered_mapint, float。一个值得注意的边界源码在 concurrent_unordered_map.h 留有// TODO: investigate if a deduction guide for concurrent_unordered_map(It, It, Alloc) is needed的注释说明“迭代器范围 仅分配器不带桶数”这一组合当前没有对应的显式推导指引使用时应显式带上桶数或改用显式模板参数。源码中的实现细节补充阅读 concurrent_unordered_map.h 时还有两处与文档互补的细节编译器缺陷规避源码末尾concurrent_unordered_map.h专门为 Apple LLVM 10.0.0 增加了一条显式推导指引#if __APPLE__ __TBB_CLANG_VERSION 100000 // An explicit deduction guide is required for copy/move constructor with allocator for APPLE LLVM 10.0.0 template typename Key, typename T, typename Hash, typename KeyEq, typename Alloc concurrent_unordered_map( concurrent_unordered_mapKey, T, Hash, KeyEq, Alloc, Alloc ) - concurrent_unordered_mapKey, T, Hash, KeyEq, Alloc; #endif注释说明该版本编译器在特定条件下无法为“带 allocator 的拷贝/移动构造”生成隐式推导指引因此用显式指引兜底。这从侧面印证了文档中“拷贝/移动构造提供隐式推导指引”一句的实际工程价值。兄弟容器的同款设计concurrent_hash_map、concurrent_map、concurrent_set等容器的推导指引采用完全相同的结构见 concurrent_hash_map.h、concurrent_map.h即“范围/初始化列表构造 SFINAE 探测迭代器与分配器”理解本文的推导机制后可以平移到这些容器上。小结concurrent_unordered_map的 CTAD 支持可以归纳为一张速查表构造入口可推导的参数默认补齐的参数迭代器范围仅首尾迭代器Key、THash / KeyEqual / Allocator 全部默认迭代器范围 桶数 Hash AllocatorKey、TKeyEqual std::equal_toKey迭代器范围 桶数 AllocatorKey、THash / KeyEqual 默认初始化列表remove_const_tKey、T按传入的 Hash / Allocator 组合补齐使用时的三条纪律迭代器须满足标准库 InputIterator 要求分配器须满足标准库 Allocator 要求传入的 Hash / KeyEqual 不能被探测为 Allocator且源码额外要求 Hash 非整型。满足这些前提即可放心依赖 deduction_guides.rst 描述的推导行为并用 concurrent_unordered_map.h 中的实际签名核对推导结果。【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考