diff --git a/doc/modules/ROOT/nav_zh_Hans.adoc b/doc/modules/ROOT/nav_zh_Hans.adoc new file mode 100644 index 0000000..2221eb9 --- /dev/null +++ b/doc/modules/ROOT/nav_zh_Hans.adoc @@ -0,0 +1,51 @@ +* xref:intro.adoc[] +* xref:buckets.adoc[] +* xref:hash_equality.adoc[] +* xref:regular.adoc[] +* xref:concurrent.adoc[] +* xref:hash_quality.adoc[] +* xref:compliance.adoc[合规性] +* xref:structures.adoc[结构] +* xref:debuggability.adoc[可调试性] +* xref:benchmarks.adoc[基准测试] +* xref:rationale.adoc[设计依据] +* xref:ref.adoc[参考文档] +** xref:reference/header_unordered_map_fwd.adoc[``] +** xref:reference/header_unordered_map_top.adoc[``] +** xref:reference/header_unordered_map.adoc[``] +** xref:reference/unordered_map.adoc[`unordered_map`] +** xref:reference/unordered_multimap.adoc[`unordered_multimap`] +** xref:reference/header_unordered_set_fwd.adoc[``] +** xref:reference/header_unordered_set_top.adoc[``] +** xref:reference/header_unordered_set.adoc[``] +** xref:reference/unordered_set.adoc[`unordered_set`] +** xref:reference/unordered_multiset.adoc[`unordered_multiset`] +** xref:reference/hash_traits.adoc[哈希特征] +** xref:reference/stats.adoc[统计信息] +** xref:reference/header_unordered_flat_map_fwd.adoc[``] +** xref:reference/header_unordered_flat_map.adoc[``] +** xref:reference/unordered_flat_map.adoc[`unordered_flat_map`] +** xref:reference/header_unordered_flat_set_fwd.adoc[``] +** xref:reference/header_unordered_flat_set.adoc[``] +** xref:reference/unordered_flat_set.adoc[`unordered_flat_set`] +** xref:reference/header_unordered_node_map_fwd.adoc[``] +** xref:reference/header_unordered_node_map.adoc[``] +** xref:reference/unordered_node_map.adoc[`unordered_node_map`] +** xref:reference/header_unordered_node_set_fwd.adoc[``] +** xref:reference/header_unordered_node_set.adoc[``] +** xref:reference/unordered_node_set.adoc[`unordered_node_set`] +** xref:reference/header_concurrent_flat_map_fwd.adoc[``] +** xref:reference/header_concurrent_flat_map.adoc[``] +** xref:reference/concurrent_flat_map.adoc[`concurrent_flat_map`] +** xref:reference/header_concurrent_flat_set_fwd.adoc[``] +** xref:reference/header_concurrent_flat_set.adoc[``] +** xref:reference/concurrent_flat_set.adoc[`concurrent_flat_set`] +** xref:reference/header_concurrent_node_map_fwd.adoc[``] +** xref:reference/header_concurrent_node_map.adoc[``] +** xref:reference/concurrent_node_map.adoc[`concurrent_node_map`] +** xref:reference/header_concurrent_node_set_fwd.adoc[``] +** xref:reference/header_concurrent_node_set.adoc[``] +** xref:reference/concurrent_node_set.adoc[`concurrent_node_set`] +* xref:changes.adoc[变更记录] +* xref:bibliography.adoc[参考文献] +* xref:copyright.adoc[版权声明] diff --git a/doc/modules/ROOT/pages/benchmarks_zh_Hans.adoc b/doc/modules/ROOT/pages/benchmarks_zh_Hans.adoc new file mode 100644 index 0000000..a6cca2f --- /dev/null +++ b/doc/modules/ROOT/pages/benchmarks_zh_Hans.adoc @@ -0,0 +1,553 @@ +[#benchmarks] +:idprefix: benchmarks_ + += 基准测试 + +== boost::unordered_[multi]set + +所有基准测试均使用 `unordered++_++set++<++unsigned int++>++`(非重复元素) 和 `unordered++_++multiset++<++unsigned int++>++` (重复元素)创建。源代码可 https://github.com/boostorg/boost_unordered_benchmarks/tree/boost_unordered_set[在此处获取] 。 + +插入基准测试会插入 `n` 个随机值,其中 `n` 的范围在10,000至300万之间。在重复值测试场景中,同一组随机值会平均重复插入 5 次。 + +在擦除基准测试中,随机删除所有 `n` 个元素直至容器为空。通过键擦除操作使用 `erase(const key++_++type&)` 方法,每次调用将移除整个等效元素组。 + +成功查找基准测试通过按照元素的原始插入顺序查找全部 `n` 个值来完成。 + +未命中查找基准测试使用不同种子值生成的 `n` 个随机整数进行。 + +=== GCC 12 + libstdc++-v3, x64 + +==== 插入 + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-set/gcc/running insertion.xlsx.practice.png[width=250,link=_images/benchmarks-set/gcc/running insertion.xlsx.practice.png,window=_blank] +|image::benchmarks-set/gcc/running insertion.xlsx.practice non-unique.png[width=250,link=_images/benchmarks-set/gcc/running insertion.xlsx.practice non-unique.png,window=_blank] +|image::benchmarks-set/gcc/running insertion.xlsx.practice non-unique 5.png[width=250,link=_images/benchmarks-set/gcc/running insertion.xlsx.practice non-unique 5.png,window=_blank] + +h|非重复元素 h|重复元素 h|重复元素, + 最大负载因子 5 |=== + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-set/gcc/running insertion.xlsx.practice norehash.png[width=250,link=_images/benchmarks-set/gcc/running insertion.xlsx.practice norehash.png,window=_blank] +|image::benchmarks-set/gcc/running insertion.xlsx.practice norehash non-unique.png[width=250,link=_images/benchmarks-set/gcc/running insertion.xlsx.practice norehash non-unique.png,window=_blank] +|image::benchmarks-set/gcc/running insertion.xlsx.practice norehash non-unique 5.png[width=250,link=_images/benchmarks-set/gcc/running insertion.xlsx.practice norehash non-unique 5.png,window=_blank] + +h|非重复元素, + 预先 `reserve` h|重复元素, + 预先 `reserve` h|重复元素, + 最大负载因子 5, + 预先 `reserve` + +|=== + +==== 擦除 + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-set/gcc/scattered erasure.xlsx.practice.png[width=250,link=_images/benchmarks-set/gcc/scattered erasure.xlsx.practice.png,window=_blank] +|image::benchmarks-set/gcc/scattered erasure.xlsx.practice non-unique.png[width=250,link=_images/benchmarks-set/gcc/scattered erasure.xlsx.practice non-unique.png,window=_blank] +|image::benchmarks-set/gcc/scattered erasure.xlsx.practice non-unique 5.png[width=250,link=_images/benchmarks-set/gcc/scattered erasure.xlsx.practice non-unique 5.png,window=_blank] + +h|非重复元素 h|重复元素 h|重复元素, + 最大负载因子 5 + +| +|image::benchmarks-set/gcc/scattered erasure by key.xlsx.practice non-unique.png[width=250,link=_images/benchmarks-set/gcc/scattered erasure by key.xlsx.practice non-unique.png,window=_blank] +|image::benchmarks-set/gcc/scattered erasure by key.xlsx.practice non-unique 5.png[width=250,link=_images/benchmarks-set/gcc/scattered erasure by key.xlsx.practice non-unique 5.png,window=_blank] + +| +h|通过键操作, 重复元素 h|通过键操作, 重复元素, + 最大负载因子 5 + +|=== + +==== 成功查找 + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-set/gcc/scattered successful looukp.xlsx.practice.png[width=250,window=_blank,link=_images/benchmarks-set/gcc/scattered successful looukp.xlsx.practice.png] +|image::benchmarks-set/gcc/scattered successful looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=_images/benchmarks-set/gcc/scattered successful looukp.xlsx.practice non-unique.png] +|image::benchmarks-set/gcc/scattered successful looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=_images/benchmarks-set/gcc/scattered successful looukp.xlsx.practice non-unique 5.png] + +h|非重复元素 h|重复元素 h|重复元素, + 最大负载因子 5 + +|=== + +==== 未命中查找 + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-set/gcc/scattered unsuccessful looukp.xlsx.practice.png[width=250,window=_blank,link=_images/benchmarks-set/gcc/scattered unsuccessful looukp.xlsx.practice.png] +|image::benchmarks-set/gcc/scattered unsuccessful looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=_images/benchmarks-set/gcc/scattered unsuccessful looukp.xlsx.practice non-unique.png] +|image::benchmarks-set/gcc/scattered unsuccessful looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=_images/benchmarks-set/gcc/scattered unsuccessful looukp.xlsx.practice non-unique 5.png] + +h|非重复元素 h|重复元素 h|重复元素, + 最大负载因子 5 + +|=== + +=== Clang 15 + libc++, x64 + +==== 插入 + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-set/clang_libcpp/running insertion.xlsx.practice.png[width=250, window=_blank,link=_images/benchmarks-set/clang_libcpp/running insertion.xlsx.practice.png] +|image::benchmarks-set/clang_libcpp/running insertion.xlsx.practice non-unique.png[width=250, window=_blank,link=_images/benchmarks-set/clang_libcpp/running insertion.xlsx.practice non-unique.png] +|image::benchmarks-set/clang_libcpp/running insertion.xlsx.practice non-unique 5.png[width=250, window=_blank,link=_images/benchmarks-set/clang_libcpp/running insertion.xlsx.practice non-unique 5.png] + +h|非重复元素 h|重复元素 h|重复元素, + 最大负载因子 5 + +|=== + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-set/clang_libcpp/running insertion.xlsx.practice norehash.png[width=250,window=_blank,link=_images/benchmarks-set/clang_libcpp/running insertion.xlsx.practice norehash.png] +|image::benchmarks-set/clang_libcpp/running insertion.xlsx.practice norehash non-unique.png[width=250,window=_blank,link=_images/benchmarks-set/clang_libcpp/running insertion.xlsx.practice norehash non-unique.png] +|image::benchmarks-set/clang_libcpp/running insertion.xlsx.practice norehash non-unique 5.png[width=250,window=_blank,link=_images/benchmarks-set/clang_libcpp/running insertion.xlsx.practice norehash non-unique 5.png] + +h|非重复元素, + 预先 `reserve` h|重复元素, + 预先 `reserve` h|重复元素, + 最大负载因子 5, + 预先 `reserve` + +|=== + +==== 擦除 + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-set/clang_libcpp/scattered erasure.xlsx.practice.png[width=250,window=_blank,link=_images/benchmarks-set/clang_libcpp/scattered erasure.xlsx.practice.png] +|image::benchmarks-set/clang_libcpp/scattered erasure.xlsx.practice non-unique.png[width=250,window=_blank,link=_images/benchmarks-set/clang_libcpp/scattered erasure.xlsx.practice non-unique.png] +|image::benchmarks-set/clang_libcpp/scattered erasure.xlsx.practice non-unique 5.png[width=250,window=_blank,link=_images/benchmarks-set/clang_libcpp/scattered erasure.xlsx.practice non-unique 5.png] + +h|非重复元素 h|重复元素 h|重复元素, + 最大负载因子 5 + +| +|image::benchmarks-set/clang_libcpp/scattered erasure by key.xlsx.practice non-unique.png[width=250,link=_images/benchmarks-set/clang_libcpp/scattered erasure by key.xlsx.practice non-unique.png,window=_blank] +|image::benchmarks-set/clang_libcpp/scattered erasure by key.xlsx.practice non-unique 5.png[width=250,link=_images/benchmarks-set/clang_libcpp/scattered erasure by key.xlsx.practice non-unique 5.png,window=_blank] + +| +h|通过键操作, 重复元素 h|通过键操作, 重复元素, + 最大负载因子 5 + +|=== + +==== 成功查找 + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-set/clang_libcpp/scattered successful looukp.xlsx.practice.png[width=250,window=_blank,link=_images/benchmarks-set/clang_libcpp/scattered successful looukp.xlsx.practice.png] +|image::benchmarks-set/clang_libcpp/scattered successful looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=_images/benchmarks-set/clang_libcpp/scattered successful looukp.xlsx.practice non-unique.png] +|image::benchmarks-set/clang_libcpp/scattered successful looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=_images/benchmarks-set/clang_libcpp/scattered successful looukp.xlsx.practice non-unique 5.png] + +h|非重复元素 h|重复元素 h|重复元素, + 最大负载因子 5 + +|=== + +==== 未命中查找 + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-set/clang_libcpp/scattered unsuccessful looukp.xlsx.practice.png[width=250,window=_blank,link=_images/benchmarks-set/clang_libcpp/scattered unsuccessful looukp.xlsx.practice.png] +|image::benchmarks-set/clang_libcpp/scattered unsuccessful looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=_images/benchmarks-set/clang_libcpp/scattered unsuccessful looukp.xlsx.practice non-unique.png] +|image::benchmarks-set/clang_libcpp/scattered unsuccessful looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=_images/benchmarks-set/clang_libcpp/scattered unsuccessful looukp.xlsx.practice non-unique 5.png] + +h|非重复元素 h|重复元素 h|重复元素, + 最大负载因子 5 + +|=== + +=== Visual Studio 2022 + Dinkumware, x64 + +==== 插入 + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-set/vs/running insertion.xlsx.practice.png[width=250,window=_blank,link=_images/benchmarks-set/vs/running insertion.xlsx.practice.png] +|image::benchmarks-set/vs/running insertion.xlsx.practice non-unique.png[width=250,window=_blank,link=_images/benchmarks-set/vs/running insertion.xlsx.practice non-unique.png] +|image::benchmarks-set/vs/running insertion.xlsx.practice non-unique 5.png[width=250,window=_blank,link=_images/benchmarks-set/vs/running insertion.xlsx.practice non-unique 5.png] + +h|非重复元素 h|重复元素 h|重复元素, + 最大负载因子 5 + +|=== + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-set/vs/running insertion.xlsx.practice norehash.png[width=250,window=_blank,link=_images/benchmarks-set/vs/running insertion.xlsx.practice norehash.png] +|image::benchmarks-set/vs/running insertion.xlsx.practice norehash non-unique.png[width=250,window=_blank,link=_images/benchmarks-set/vs/running insertion.xlsx.practice norehash non-unique.png] +|image::benchmarks-set/vs/running insertion.xlsx.practice norehash non-unique 5.png[width=250,window=_blank,link=_images/benchmarks-set/vs/running insertion.xlsx.practice norehash non-unique 5.png] + +h|非重复元素, + 预先 `reserve` h|重复元素, + 预先 `reserve` h|重复元素, + 最大负载因子 5, + 预先 `reserve` + +|=== + +==== 擦除 + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-set/vs/scattered erasure.xlsx.practice.png[width=250,window=_blank,link=_images/benchmarks-set/vs/scattered erasure.xlsx.practice.png] +|image::benchmarks-set/vs/scattered erasure.xlsx.practice non-unique.png[width=250,window=_blank,link=_images/benchmarks-set/vs/scattered erasure.xlsx.practice non-unique.png] +|image::benchmarks-set/vs/scattered erasure.xlsx.practice non-unique 5.png[width=250,window=_blank,link=_images/benchmarks-set/vs/scattered erasure.xlsx.practice non-unique 5.png] + +h|非重复元素 h|重复元素 h|重复元素, + 最大负载因子 5 + +| +|image::benchmarks-set/vs/scattered erasure by key.xlsx.practice non-unique.png[width=250,link=_images/benchmarks-set/vs/scattered erasure by key.xlsx.practice non-unique.png,window=_blank] +|image::benchmarks-set/vs/scattered erasure by key.xlsx.practice non-unique 5.png[width=250,link=_images/benchmarks-set/vs/scattered erasure by key.xlsx.practice non-unique 5.png,window=_blank] + +| +h|通过键操作, 重复元素 h|通过键操作, 重复元素, + 最大负载因子 5 + +|=== + +==== 成功查找 + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-set/vs/scattered successful looukp.xlsx.practice.png[width=250,window=_blank,link=_images/benchmarks-set/vs/scattered successful looukp.xlsx.practice.png] +|image::benchmarks-set/vs/scattered successful looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=_images/benchmarks-set/vs/scattered successful looukp.xlsx.practice non-unique.png] +|image::benchmarks-set/vs/scattered successful looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=_images/benchmarks-set/vs/scattered successful looukp.xlsx.practice non-unique 5.png] + +h|非重复元素 h|重复元素 h|重复元素, + 最大负载因子 5 + +|=== + +==== 未命中查找 + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-set/vs/scattered unsuccessful looukp.xlsx.practice.png[width=250,window=_blank,link=_images/benchmarks-set/vs/scattered unsuccessful looukp.xlsx.practice.png] +|image::benchmarks-set/vs/scattered unsuccessful looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=_images/benchmarks-set/vs/scattered unsuccessful looukp.xlsx.practice non-unique.png] +|image::benchmarks-set/vs/scattered unsuccessful looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=_images/benchmarks-set/vs/scattered unsuccessful looukp.xlsx.practice non-unique 5.png] + +h|非重复元素 h|重复元素 h|重复元素, + 最大负载因子 5 + +|=== + +== boost::unordered_(flat|node)_map + +所有基准测试均为使用以下链接来创建的: + +* https://abseil.io/docs/cpp/guides/container[`absl::flat++_++hash++_++map`^]`++<++uint64++_++t, uint64++_++t++>++` +* `boost::unordered_map` +* `boost::unordered_flat_map` +* `boost::unordered_node_map` + +源代码可 https://github.com/boostorg/boost_unordered_benchmarks/tree/boost_unordered_flat_map[在此处获取] 。 + +插入基准测试的操作将插入 `n` 个随机值,其中 `n` 的范围在10,000至1000万之间。 + +擦除基准测试会遍历全部 `n` 个元素,并删除其中键值为奇数的元素(平均约占50%)。 + +成功查找基准测试通过按照元素的原始插入顺序查找全部 `n` 个值来完成。 + +未命中查找基准测试使用不同种子值生成的 `n` 个随机整数进行。 + + +=== GCC 12, x64 + + +[caption=] +[cols="4*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-flat_map/gcc-x64/Running insertion.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/gcc-x64/Running insertion.xlsx.plot.png] +|image::benchmarks-flat_map/gcc-x64/Running erasure.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/gcc-x64/Running erasure.xlsx.plot.png] +|image::benchmarks-flat_map/gcc-x64/Scattered successful looukp.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/gcc-x64/Scattered successful looukp.xlsx.plot.png] +|image::benchmarks-flat_map/gcc-x64/Scattered unsuccessful looukp.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/gcc-x64/Scattered unsuccessful looukp.xlsx.plot.png] + +h|插入的执行 h|擦除的执行 h|成功查找 h|未命中查找 + +|=== + +=== Clang 15, x64 + + +[caption=] +[cols="4*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-flat_map/clang-x64/Running insertion.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/clang-x64/Running insertion.xlsx.plot.png] +|image::benchmarks-flat_map/clang-x64/Running erasure.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/clang-x64/Running erasure.xlsx.plot.png] +|image::benchmarks-flat_map/clang-x64/Scattered successful looukp.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/clang-x64/Scattered successful looukp.xlsx.plot.png] +|image::benchmarks-flat_map/clang-x64/Scattered unsuccessful looukp.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/clang-x64/Scattered unsuccessful looukp.xlsx.plot.png] + +h|插入的执行 h|擦除的执行 h|成功查找 h|未命中查找 + +|=== + +=== Visual Studio 2022, x64 + + +[caption=] +[cols="4*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-flat_map/vs-x64/Running insertion.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/vs-x64/Running insertion.xlsx.plot.png] +|image::benchmarks-flat_map/vs-x64/Running erasure.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/vs-x64/Running erasure.xlsx.plot.png] +|image::benchmarks-flat_map/vs-x64/Scattered successful looukp.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/vs-x64/Scattered successful looukp.xlsx.plot.png] +|image::benchmarks-flat_map/vs-x64/Scattered unsuccessful looukp.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/vs-x64/Scattered unsuccessful looukp.xlsx.plot.png] + +h|插入的执行 h|擦除的执行 h|成功查找 h|未命中查找 + +|=== + +=== Clang 12, ARM64 + + +[caption=] +[cols="4*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-flat_map/clang-arm64/Running insertion.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/clang-arm64/Running insertion.xlsx.plot.png] +|image::benchmarks-flat_map/clang-arm64/Running erasure.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/clang-arm64/Running erasure.xlsx.plot.png] +|image::benchmarks-flat_map/clang-arm64/Scattered successful looukp.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/clang-arm64/Scattered successful looukp.xlsx.plot.png] +|image::benchmarks-flat_map/clang-arm64/Scattered unsuccessful looukp.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/clang-arm64/Scattered unsuccessful looukp.xlsx.plot.png] + +h|插入的执行 h|擦除的执行 h|成功查找 h|未命中查找 + +|=== + +=== GCC 12, x86 + + +[caption=] +[cols="4*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-flat_map/gcc-x86/Running insertion.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/gcc-x86/Running insertion.xlsx.plot.png] +|image::benchmarks-flat_map/gcc-x86/Running erasure.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/gcc-x86/Running erasure.xlsx.plot.png] +|image::benchmarks-flat_map/gcc-x86/Scattered successful looukp.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/gcc-x86/Scattered successful looukp.xlsx.plot.png] +|image::benchmarks-flat_map/gcc-x86/Scattered unsuccessful looukp.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/gcc-x86/Scattered unsuccessful looukp.xlsx.plot.png] + +h|插入的执行 h|擦除的执行 h|成功查找 h|未命中查找 + +|=== + +=== Clang 15, x86 + + +[caption=] +[cols="4*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-flat_map/clang-x86/Running insertion.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/clang-x86/Running insertion.xlsx.plot.png] +|image::benchmarks-flat_map/clang-x86/Running erasure.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/clang-x86/Running erasure.xlsx.plot.png] +|image::benchmarks-flat_map/clang-x86/Scattered successful looukp.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/clang-x86/Scattered successful looukp.xlsx.plot.png] +|image::benchmarks-flat_map/clang-x86/Scattered unsuccessful looukp.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/clang-x86/Scattered unsuccessful looukp.xlsx.plot.png] + +h|插入的执行 h|擦除的执行 h|成功查找 h|未命中查找 + +|=== + +=== Visual Studio 2022, x86 + + +[caption=] +[cols="4*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-flat_map/vs-x86/Running insertion.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/vs-x86/Running insertion.xlsx.plot.png] +|image::benchmarks-flat_map/vs-x86/Running erasure.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/vs-x86/Running erasure.xlsx.plot.png] +|image::benchmarks-flat_map/vs-x86/Scattered successful looukp.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/vs-x86/Scattered successful looukp.xlsx.plot.png] +|image::benchmarks-flat_map/vs-x86/Scattered unsuccessful looukp.xlsx.plot.png[width=250,window=_blank,link=_images/benchmarks-flat_map/vs-x86/Scattered unsuccessful looukp.xlsx.plot.png] + +h|插入的执行 h|擦除的执行 h|成功查找 h|未命中查找 + +|=== + +== boost::concurrent_(flat|node)_map + +所有基准测试均为使用以下链接来创建的: + +* `https://spec.oneapi.io/versions/latest/elements/oneTBB/source/containers/concurrent_hash_map_cls.html[oneapi::tbb::concurrent_hash_map^]` +* `https://github.com/greg7mdp/gtl/blob/main/docs/phmap.md[gtl::parallel_flat_hash_map^]` (含64个子映射) +* `boost::concurrent_flat_map` +* `boost::concurrent_node_map` + +源代码可 https://github.com/boostorg/boost_unordered_benchmarks/tree/boost_concurrent_flat_map[在此处获取] 。 + +基准测试使用__T__个线程(数量1至16之间)并发执行随机选择的操作,这些操作包括**更新**、**成功查找**和**未命中查找**三种类型。操作使用的键遵循 https://en.wikipedia.org/wiki/Zipf%27s_law#Formal_definition[齐夫分布] ,并采用不同的__偏斜__参数:偏斜值越高,键的分布越集中在取值区间的较低数值区域。 + +`boost::concurrent_flat_map` 和 `boost::concurrent_node_map` 分别通过常规访问和 xref:concurrent.adoc#concurrent_bulk_visitation[批量访问] 进行演练:在批量访问的情况下,查找键会缓存在本地数组中,每当缓冲区大小达到 `xref:reference/concurrent_flat_map.adoc#concurrent_flat_map_constants[bulk_visit_size]` 时,再一次性处理这些键。 + +=== GCC 12, x64 + + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-concurrent_map/gcc-x64/Parallel workload.xlsx.500k, 0.01.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/gcc-x64/Parallel workload.xlsx.500k, 0.01.png"] +|image::benchmarks-concurrent_map/gcc-x64/Parallel workload.xlsx.500k, 0.5.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/gcc-x64/Parallel workload.xlsx.500k, 0.5.png"] +|image::benchmarks-concurrent_map/gcc-x64/Parallel workload.xlsx.500k, 0.99.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/gcc-x64/Parallel workload.xlsx.500k, 0.99.png"] + +h|50万次更新, 450万次查找 + 偏斜=0.01 h|50万次更新, 450万次查找 + 偏斜=0.5 h|50万次更新, 450万次查找 + 偏斜=0.99 |=== + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-concurrent_map/gcc-x64/Parallel workload.xlsx.5M, 0.01.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/gcc-x64/Parallel workload.xlsx.5M, 0.01.png"] +|image::benchmarks-concurrent_map/gcc-x64/Parallel workload.xlsx.5M, 0.5.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/gcc-x64/Parallel workload.xlsx.5M, 0.5.png"] +|image::benchmarks-concurrent_map/gcc-x64/Parallel workload.xlsx.5M, 0.99.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/gcc-x64/Parallel workload.xlsx.5M, 0.99.png"] + +h|500万次更新, 4500万次查找 + 偏斜=0.01 h|500万次更新, 4500万次查找 + 偏斜=0.5 h|500万次更新, 4500万次查找 + 偏斜=0.99 |=== + +=== Clang 15, x64 + + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-concurrent_map/clang-x64/Parallel workload.xlsx.500k, 0.01.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-x64/Parallel workload.xlsx.500k, 0.01.png"] +|image::benchmarks-concurrent_map/clang-x64/Parallel workload.xlsx.500k, 0.5.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-x64/Parallel workload.xlsx.500k, 0.5.png"] +|image::benchmarks-concurrent_map/clang-x64/Parallel workload.xlsx.500k, 0.99.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-x64/Parallel workload.xlsx.500k, 0.99.png"] + +h|50万次更新, 450万次查找 + 偏斜=0.01 h|50万次更新, 450万次查找 + 偏斜=0.5 h|50万次更新, 450万次查找 + 偏斜=0.99 |=== + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-concurrent_map/clang-x64/Parallel workload.xlsx.5M, 0.01.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-x64/Parallel workload.xlsx.5M, 0.01.png"] +|image::benchmarks-concurrent_map/clang-x64/Parallel workload.xlsx.5M, 0.5.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-x64/Parallel workload.xlsx.5M, 0.5.png"] +|image::benchmarks-concurrent_map/clang-x64/Parallel workload.xlsx.5M, 0.99.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-x64/Parallel workload.xlsx.5M, 0.99.png"] + +h|500万次更新, 4500万次查找 + 偏斜=0.01 h|500万次更新, 4500万次查找 + 偏斜=0.5 h|500万次更新, 4500万次查找 + 偏斜=0.99 |=== + +=== Visual Studio 2022, x64 + + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-concurrent_map/vs-x64/Parallel workload.xlsx.500k, 0.01.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/vs-x64/Parallel workload.xlsx.500k, 0.01.png"] +|image::benchmarks-concurrent_map/vs-x64/Parallel workload.xlsx.500k, 0.5.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/vs-x64/Parallel workload.xlsx.500k, 0.5.png"] +|image::benchmarks-concurrent_map/vs-x64/Parallel workload.xlsx.500k, 0.99.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/vs-x64/Parallel workload.xlsx.500k, 0.99.png"] + +h|50万次更新, 450万次查找 + 偏斜=0.01 h|50万次更新, 450万次查找 + 偏斜=0.5 h|50万次更新, 450万次查找 + 偏斜=0.99 |=== + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-concurrent_map/vs-x64/Parallel workload.xlsx.5M, 0.01.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/vs-x64/Parallel workload.xlsx.5M, 0.01.png"] +|image::benchmarks-concurrent_map/vs-x64/Parallel workload.xlsx.5M, 0.5.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/vs-x64/Parallel workload.xlsx.5M, 0.5.png"] +|image::benchmarks-concurrent_map/vs-x64/Parallel workload.xlsx.5M, 0.99.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/vs-x64/Parallel workload.xlsx.5M, 0.99.png"] + +h|500万次更新, 4500万次查找 + 偏斜=0.01 h|500万次更新, 4500万次查找 + 偏斜=0.5 h|500万次更新, 4500万次查找 + 偏斜=0.99 |=== + +=== Clang 12, ARM64 + + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-concurrent_map/clang-arm64/Parallel workload.xlsx.500k, 0.01.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-arm64/Parallel workload.xlsx.500k, 0.01.png"] +|image::benchmarks-concurrent_map/clang-arm64/Parallel workload.xlsx.500k, 0.5.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-arm64/Parallel workload.xlsx.500k, 0.5.png"] +|image::benchmarks-concurrent_map/clang-arm64/Parallel workload.xlsx.500k, 0.99.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-arm64/Parallel workload.xlsx.500k, 0.99.png"] + +h|50万次更新, 450万次查找 + 偏斜=0.01 h|50万次更新, 450万次查找 + 偏斜=0.5 h|50万次更新, 450万次查找 + 偏斜=0.99 |=== + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-concurrent_map/clang-arm64/Parallel workload.xlsx.5M, 0.01.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-arm64/Parallel workload.xlsx.5M, 0.01.png"] +|image::benchmarks-concurrent_map/clang-arm64/Parallel workload.xlsx.5M, 0.5.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-arm64/Parallel workload.xlsx.5M, 0.5.png"] +|image::benchmarks-concurrent_map/clang-arm64/Parallel workload.xlsx.5M, 0.99.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-arm64/Parallel workload.xlsx.5M, 0.99.png"] + +h|500万次更新, 4500万次查找 + 偏斜=0.01 h|500万次更新, 4500万次查找 + 偏斜=0.5 h|500万次更新, 4500万次查找 + 偏斜=0.99 |=== + +=== GCC 12, x86 + + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-concurrent_map/gcc-x86/Parallel workload.xlsx.500k, 0.01.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/gcc-x86/Parallel workload.xlsx.500k, 0.01.png"] +|image::benchmarks-concurrent_map/gcc-x86/Parallel workload.xlsx.500k, 0.5.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/gcc-x86/Parallel workload.xlsx.500k, 0.5.png"] +|image::benchmarks-concurrent_map/gcc-x86/Parallel workload.xlsx.500k, 0.99.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/gcc-x86/Parallel workload.xlsx.500k, 0.99.png"] + +h|50万次更新, 450万次查找 + 偏斜=0.01 h|50万次更新, 450万次查找 + 偏斜=0.5 h|50万次更新, 450万次查找 + 偏斜=0.99 |=== + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-concurrent_map/gcc-x86/Parallel workload.xlsx.5M, 0.01.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/gcc-x86/Parallel workload.xlsx.5M, 0.01.png"] +|image::benchmarks-concurrent_map/gcc-x86/Parallel workload.xlsx.5M, 0.5.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/gcc-x86/Parallel workload.xlsx.5M, 0.5.png"] +|image::benchmarks-concurrent_map/gcc-x86/Parallel workload.xlsx.5M, 0.99.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/gcc-x86/Parallel workload.xlsx.5M, 0.99.png"] + +h|500万次更新, 4500万次查找 + 偏斜=0.01 h|500万次更新, 4500万次查找 + 偏斜=0.5 h|500万次更新, 4500万次查找 + 偏斜=0.99 |=== + +=== Clang 15, x86 + + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-concurrent_map/clang-x86/Parallel workload.xlsx.500k, 0.01.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-x86/Parallel workload.xlsx.500k, 0.01.png"] +|image::benchmarks-concurrent_map/clang-x86/Parallel workload.xlsx.500k, 0.5.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-x86/Parallel workload.xlsx.500k, 0.5.png"] +|image::benchmarks-concurrent_map/clang-x86/Parallel workload.xlsx.500k, 0.99.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-x86/Parallel workload.xlsx.500k, 0.99.png"] + +h|50万次更新, 450万次查找 + 偏斜=0.01 h|50万次更新, 450万次查找 + 偏斜=0.5 h|50万次更新, 450万次查找 + 偏斜=0.99 |=== + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-concurrent_map/clang-x86/Parallel workload.xlsx.5M, 0.01.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-x86/Parallel workload.xlsx.5M, 0.01.png"] +|image::benchmarks-concurrent_map/clang-x86/Parallel workload.xlsx.5M, 0.5.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-x86/Parallel workload.xlsx.5M, 0.5.png"] +|image::benchmarks-concurrent_map/clang-x86/Parallel workload.xlsx.5M, 0.99.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/clang-x86/Parallel workload.xlsx.5M, 0.99.png"] + +h|500万次更新, 4500万次查找 + 偏斜=0.01 h|500万次更新, 4500万次查找 + 偏斜=0.5 h|500万次更新, 4500万次查找 + 偏斜=0.99 |=== + +=== Visual Studio 2022, x86 + + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-concurrent_map/vs-x86/Parallel workload.xlsx.500k, 0.01.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/vs-x86/Parallel workload.xlsx.500k, 0.01.png"] +|image::benchmarks-concurrent_map/vs-x86/Parallel workload.xlsx.500k, 0.5.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/vs-x86/Parallel workload.xlsx.500k, 0.5.png"] +|image::benchmarks-concurrent_map/vs-x86/Parallel workload.xlsx.500k, 0.99.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/vs-x86/Parallel workload.xlsx.500k, 0.99.png"] + +h|50万次更新, 450万次查找 + 偏斜=0.01 h|50万次更新, 450万次查找 + 偏斜=0.5 h|50万次更新, 450万次查找 + 偏斜=0.99 |=== + +[caption=] +[cols="3*^.^a", frame=all, grid=all] +|=== + +|image::benchmarks-concurrent_map/vs-x86/Parallel workload.xlsx.5M, 0.01.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/vs-x86/Parallel workload.xlsx.5M, 0.01.png"] +|image::benchmarks-concurrent_map/vs-x86/Parallel workload.xlsx.5M, 0.5.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/vs-x86/Parallel workload.xlsx.5M, 0.5.png"] +|image::benchmarks-concurrent_map/vs-x86/Parallel workload.xlsx.5M, 0.99.png[width=250,window=_blank,link="_images/benchmarks-concurrent_map/vs-x86/Parallel workload.xlsx.5M, 0.99.png"] + +h|500万次更新, 4500万次查找 + 偏斜=0.01 h|500万次更新, 4500万次查找 + 偏斜=0.5 h|500万次更新, 4500万次查找 + 偏斜=0.99 |=== diff --git a/doc/modules/ROOT/pages/bibliography_zh_Hans.adoc b/doc/modules/ROOT/pages/bibliography_zh_Hans.adoc new file mode 100644 index 0000000..4c4b0b4 --- /dev/null +++ b/doc/modules/ROOT/pages/bibliography_zh_Hans.adoc @@ -0,0 +1,12 @@ +[#bibliography] + +:idprefix: bibliography_ + += 文献目录 + +* _C/C++ Users Journal_. February, 2006. Pete Becker. http://www.ddj.com/cpp/184402066[STL and TR1: :第三部分 — 无序容器^] 。 + +关于标准无序容器的介绍性文章。 +* __维基百科__中的 https://en.wikipedia.org/wiki/Hash_table[哈希表^] 。 + +这是关于哈希表实现原理的概述,重点探讨了链地址法和开放地址法两种实现路径的差异。 +* Peter Dimov,2022年。 https://pdimov.github.io/articles/unordered_dev_plan.html[Boost.Unordered库开发计划^] 。 + diff --git a/doc/modules/ROOT/pages/buckets_zh_Hans.adoc b/doc/modules/ROOT/pages/buckets_zh_Hans.adoc new file mode 100644 index 0000000..ceef73e --- /dev/null +++ b/doc/modules/ROOT/pages/buckets_zh_Hans.adoc @@ -0,0 +1,101 @@ +[#buckets] +:idprefix: buckets_ + += 哈希表基础 + +该容器由若干__桶__组成,每个桶可容纳任意数量的元素。例如,下图展示了一个包含7个桶的 xref:reference/unordered_set.adoc#unordered_set[boost::unordered_set],其中存储着5个元素( `A` 、 `B` 、 `C` 、 `D` 和 `E` )(此示意图仅为演示用途,实际容器通常包含更多桶)。 + +image::buckets.png[] + +容器通过哈希函数 `Hash` 作用于元素的键来确定其所属的桶(对于集合而言,键即元素本身,但为统一集合与映射表的术语仍称作键)。该函数返回 `std::size_t` 类型的值。由于 `std::size_t` 的取值范围远大于桶的数量,容器会对此值进行二次转换以确定元素应存入的桶。 + +根据指定键检索元素的过程非常简单:首先对键执行相同处理以定位对应桶,随后通过相等性谓词 `Pred` 将键与桶内元素进行比较以寻找匹配项。若哈希函数表现良好,元素将均匀分布于各桶中,此时仅需检查少量元素即可完成检索。 + +关于哈希函数与相等性谓词的详细说明请参阅 xref:hash_equality.adoc#hash_equality[后续章节] 。 + +如图所示, `A` 与 `D` 被置于同一桶内。在此桶内查找元素时最多需要进行 2 次比对,这会降低检索效率,该现象称为**冲突**。为维持高效运作,我们需尽可能减少冲突的发生。 + +若使用 xref:reference/unordered_flat_set.adoc[boost::unordered_flat_set]替代 `boost::unordered_set` ,其结构示意图将呈现如下形式: + +image::buckets-oa.png[] + +在开放寻址式容器中,每个桶最多只能容纳一个元素;若发生冲突(如示例中的元素 `D` 的情况),该元素将使用原始位置附近的其他可用桶。基于此简化设计,Boost.Unordered开放寻址容器仅提供极其有限的桶访问API。 + +[caption=, title='Table {counter:table-counter}. Methods for Accessing Buckets'] +[cols="1,.^1", frame=all, grid=rows] +|=== +2+^h| *所有容器* h|*方法* h|*描述* + +|`size_type bucket_count() const` +|The number of buckets. + +2+^h| *仅限闭寻址容器* h|*方法* h|*描述* + +|`size_type max_bucket_count() const` +|An upper bound on the number of buckets. +|`size_type bucket_size(size_type n) const` +|The number of elements in bucket `n`. + +|`size_type bucket(key_type const& k) const` +|Returns the index of the bucket which would contain `k`. + +|`local_iterator begin(size_type n)` +1.6+|返回编号为 `n` 的桶的起始与末尾迭代器。 + +|`local_iterator end(size_type n)` + +|`const_local_iterator begin(size_type n) const` + +|`const_local_iterator end(size_type n) const` + +|`const_local_iterator cbegin(size_type n) const` + +|`const_local_iterator cend(size_type n) const` + +|=== + +== 桶数量控制 + +当无序关联容器中的元素不断增加时,冲突次数会随之上升,从而导致性能下降。为解决此问题,容器会在插入元素过程中自动增加桶的数量。用户也可以通过调用 `rehash` 方法来按需调整容器的桶数量。 + +标准规范虽未强制规定桶数量的具体选择方式,但基于容器的__负载因子__(即元素数量与桶数量的比值)提出了若干要求。同时,容器还设有一个__最大负载因子__,其运行过程中需始终将实际负载因子维持在该阈值以下。 + +用户无法直接控制桶的数量,但可通过以下两种方式间接调控: + +* 用户可在构造容器或调用 `rehash` 方法时指定桶数量的最小值。 +* 用户可通过调用 `max_load_factor` 方法来设定最大负载因子的建议值。 + +`max_load_factor` 并不允许用户自行设定最大负载因子,该方法仅用于提供__提示__。即便如此,标准也并未强制要求容器必须严格遵守该数值。唯一强制要求负载因子必须小于最大值的场景是在调用 `rehash` 方法之后。但大多数实现会尝试将元素数量维持在最大负载因子以下,并将最大负载因子设定为与提示值相同或接近——除非用户提供的提示值过小或过大。 + +[caption=, title='Table {counter:table-counter}. Methods for Controlling Bucket Size'] +[cols="1,.^1", frame=all, grid=rows] +|=== +2+^h| *所有容器* h|*方法* h|*描述* + +|`X(size_type n)` +|Construct an empty container with at least `n` buckets (`X` is the container type). + +|`X(InputIterator i, InputIterator j, size_type n)` +|Construct an empty container with at least `n` buckets and insert elements from the range `[i, j)` (`X` is the container type). + +|`float load_factor() const` +|The average number of elements per bucket. + +|`float max_load_factor() const` +|Returns the current maximum load factor. + +|`float max_load_factor(float z)` +|Changes the container's maximum load factor, using `z` as a hint. + +**开放寻址与并发容器:**此函数无效,用户不允许更改最大负载因子。 + +|`void rehash(size_type n)` +|Changes the number of buckets so that there at least `n` buckets, and so that the load factor is less than the maximum load factor. + +2+^h| *仅限开放寻址与并发容器* h|*方法* h|*描述* + +|`size_type max_load() const` +|Returns the maximum number of allowed elements in the container before rehash. + +|=== + +关于开放寻址与并发容器的 `max_load` 注意事项:在容器创建或执行 `rehash` 后,最大负载值将保持为 `max_load_factor() * bucket_count()` ;但在高负载场景下执行元素删除操作时,该值可能轻微下降。例如,若某个 xref:reference/unordered_flat_map.adoc#unordered_flat_map[boost::unordered_flat_map] 的 `size()` 已接近 `max_load()` 阈值,随后删除 1,000 个元素时, `max_load()` 可能减少约数十个元素。此机制是Boost.Unordered为保持性能稳定而采取的内部机制,用户在规划无重组插入操作时需予以考虑。 diff --git a/doc/modules/ROOT/pages/changes_zh_Hans.adoc b/doc/modules/ROOT/pages/changes_zh_Hans.adoc new file mode 100644 index 0000000..c5b4702 --- /dev/null +++ b/doc/modules/ROOT/pages/changes_zh_Hans.adoc @@ -0,0 +1,392 @@ +[#changes] += 变更日志 + +:idprefix: changes_ :svn-ticket-url: https://svn.boost.org/trac/boost/ticket :github-pr-url: https://github.com/boostorg/unordered/pull :cpp: C++ + +== 版本 1.89.0 + +* 已弃用的 boost::unordered::hash_is_avalanching 现在是 +在 中对 boost::hash_is_avalanching 的使用声明。请直接使用该头文件代替。 将在未来被移除。 +* 为开放寻址容器新增 `pull(const_iterator)` 操作,该操作 +支持通过移动构造机制高效移除并检索指定元素。 + +== 版本 1.88.0 + +* 使用 Antora 将文档重构为多页面格式。 + +== 版本 1.87.0 - 重大更新 + +* 新增基于节点的并发容器 `boost::concurrent_node_map` 与 `boost::concurrent_node_set` 。 +* 为并发容器新增 `insert_and_visit(x, f1, f2)` 及类似操作,这些操作 +支持在插入元素后立即对其进行访问(而 `insert_or_visit(x, f)` 仅在未执行插入时才访问元素)。 +* 在特定 `boost::concurrent_flat_set` 操作中启用独占锁访问机制, +以支持对元素进行安全的可变修改(https://github.com/boostorg/unordered/pull/265[PR#265])。 +* 在 Visual Studio Natvis 中,现支持使用带花式指针分配器的任意容器。只要为花式指针类型编写了相应的 Natvis 自定义“Intrinsic”函数,即可适用于所有花式指针类型。 +* 为所有容器和迭代器添加 GDB 美化打印器。对于使用花式指针分配器的容器,需先为花式指针类型编写对应的美化打印器方可正常使用。 +* 修复开放寻址容器中 `std::initializer_list` 的 赋值问题 +(https://github.com/boostorg/unordered/pull/277[PR#277])。 +* 通过内部将可调用对象的 `std::reference_wrapper` 传递给迭代器对重载,现允许向并发容器的 `insert_{and|or}_[c]visit` 的 `std::initializer_list` 重载传递不可复制的可调用对象。 + + +== 版本 1.86.0 + +* 当头文件 `` 可用时,新增容器 `pmr` 别名。 `boost::unordered::pmr::[container]` 别名指向使用 `std::pmr::polymorphic_allocator` 分配器类型的 `boost::unordered::[container]` 。 +* 为开放寻址容器与并发容器增设内部统计功能,可计算并提供受哈希函数质量影响的统计指标。通过全局宏 `BOOST_UNORDERED_ENABLE_STATS` 启用。 +* 雪崩哈希函数现在必须通过内嵌 `value` 常量设为 `true` 的 `is_avalanching` typedef 来标记(通常将 `is_avalanching` 定义为 `std::true_type`)。 `using is_avalanching = void` 已被弃用,但为了向后兼容性仍予保留。 +* 为容器和迭代器添加 Visual Studio Natvis 框架的自定义可视化功能。此功能适用于所有使用原始指针分配器的容器。在此版本中,若容器或迭代器的分配器使用花式指针,则暂不支持该功能,此问题可能在后续版本中解决。 + +== 版本 1.85.0 + +* 优化 `emplace()` 对 `value_type` 或 `init_type` (如适用)参数的实现,使其无需创建中间对象即可直接处理参数,因为该参数类型与待构造的中间对象类型完全相同。 +* 优化 map 容器的 `emplace()` 对 `k,v` 参数的处理:将对象构造延迟到确认需要插入元素时执行。此优化在映射的 `key_type` 可移动构造或 `k` 参数为 `key_type` 类型时生效。 +* 修复对含 `explicit` 复制构造函数的分配器的支持(https://github.com/boostorg/unordered/pull/234[PR#234])。 +* 修复 `unordered_multimap::find(k, hash, eq)` 的 `const` 版本中的缺陷(https://github.com/boostorg/unordered/pull/238[PR#238])。 + +== 版本 1.84.0 - 重大更新 + +* 新增 `boost::concurrent_flat_set` 。 +* 为并发容器新增 `[c]visit_while` 操作, +提供串行与并行两种执行模式。 +* 实现高效双向移动构造(从 +`boost::unordered_flat_(map|set)` 到 `boost::concurrent_flat_(map|set)` 及反向的) +* 为并发容器新增批量访问功能以提升查找性能。 +* 新增调试模式机制,用于检测用户代码对 +并发容器的非法重入操作。 +* 为所有容器及其(非本地)迭代器类型添加 Boost.Serialization 支持。 +* 为开放寻址容器与并发容器新增对花式指针的支持,此特性支持诸如使用 Boost.Interprocess 分配器在共享内存中构建容器等应用场景。 +这使得像使用 Boost.Interprocess 分配器在共享内存中构造容器这样的场景成为可能。 +* 修复闭寻址容器局部迭代器的成员指针运算符缺陷 +(https://github.com/boostorg/unordered/pull/221[PR#221] ,致谢 GitHub 用户 vslashg 发现并修复)。 +* 从此版本起, `boost::unordered_[multi]set` 和 `boost::unordered_[multi]map` +仅支持 C{plus}{plus}11 及以上版本。 + +== 版本 1.83.0 - 重大更新 + +* 新增基于开放寻址的快速线程安全哈希映射容器 `boost::concurrent_flat_map` 。 +* 提升开放寻址容器的迭代性能。 +* 在开放寻址容器中,原无返回值的 `erase(iterator)` 方法,现在 +返回一个可转换为下一元素迭代器的代理对象。此改进支持典型的 `it = c.erase(it)` 编程范式,且当未使用返回的代理对象时不会产生任何性能开销。 + +== 版本 1.82.0 - 重大更新 + +* 计划弃用 C{plus}{plus}03 支持。Boost 1.84.0 将不再支持 +C{plus}{plus}03 模式,C{plus}{plus}11 将成为使用该库的最低要求。 +* 新增基于节点的开放寻址容器 +`boost::unordered_node_map` 与 `boost::unordered_node_set`。 +* 将异构查找功能扩展至更多成员函数 +(根据https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2363r5.html[P2363] 规范) 。 +* 取代开放寻址容器原有的后混合处理流程(采用 +基于常量扩展乘法的新算法)。 +* 修复内部 emplace() 实现中存在的缺陷: +该缺陷曾导致栈局部类型未能使用容器的分配器进行正确构造,从而违反分配器感知构造规范。 + +== 版本 1.81.0 - 重大更新 + +* 新增基于开放寻址的快速容器`boost::unordered_flat_map` and `boost::unordered_flat_set`。 +(基于开放寻址的) +* 为所有容器添加 CTAD 推导指引。 +* 补充 https://cplusplus.github.io/LWG/issue2713[LWG 问题 2713] 中要求的缺失构造函数。 + +== 版本 1.80.0 - 重大更新 + +* 重构内部实现以显著提升性能 +* 允许 使用 `final` 修饰的 Hasher 和 KeyEqual 对象 +* 更新文档,新增性能基准对比图及关于新内部数据结构的说明 +(数据结构) + +== 版本 1.79.0 + +* 增强 C{plus}{plus}20 标准兼容性: +** 所有容器均已完成升级,支持异构查找操作 `count` 、 `equal_range` 及 `find` 。 ** 所有容器现均已实现成员函数 `contains` 。 ** 所有容器均已实现 `erase_if` 功能。 +* 增强 C{plus}{plus}23 标准兼容性: +** 所有容器均已完成升级,支持异构版本的 `erase` 与 `extract` 操作。 +* 更改 `reserve` 的行为, +使其立即分配桶数组空间(https://github.com/boostorg/unordered/pull/59[PR#59])。 +* 测试套件中的多项警告修复。 +* 将内部代码更新为使用 `boost::allocator_traits` 。 +* 切换至斐波那契哈希算法。 +* 将文档编写格式从 QuickBook 切换为 AsciiDoc。 + +== 版本 1.67.0 + +* 增强 C{plus}{plus}17 标准兼容性: +** 新增标准的模板推导指南。 ** 在节点句柄中采用简化的 `optional` 实现,以使其 更符合标准规范。 ** 为 `swap` 、 `operator=` 及节点句柄补充缺失的 `noexcept` 规范,调整实现以符合规范要求。在实现中使用 `std::allocator_traits::is_always_equal` (若该特性不可用则采用自有实现),以及 `boost::is_nothrow_swappable` 在实现中。 +* 增强 C{plus}{plus}20 标准兼容性: +** 使用具有 C{plus}{plus}20 提案语义的 `boost::to_address` , 来替代原有的自定义实现。 +* 向迭代器添加 `element_type` 类型定义,以支持 `std::pointer_traits` 的 +正常工作。 +* 在新版 Visual C{plus}{plus} 及 +其他使用 Dinkumware 标准库的环境中使用 `std::piecewise_construct` ,现在使用 Boost.Predef 来检查编译器和标准库版本。 +* 使用 `std::iterator++_++traits` 替代 boost 迭代器特征库, +以消除对 Boost.Iterator 的依赖。 +* 移除迭代器对 `std::iterator` 的继承 +(该基类在 C{plus}{plus}17 中已弃用),感谢 Daniela Engert的贡献(https://github.com/boostorg/unordered/pull/7[PR#7])。 +* 停止使用 `BOOST_DEDUCED_TYPENAME` 。 +* 更新部分 Boost 头文件包含路径。 +* 重命名部分内部方法和变量。 +* 多项测试改进。 +* 多项内部变更。 + +== 版本 1.66.0 + +* 更简化的移动构造实现。 +* 文档修正(https://github.com/boostorg/unordered/pull/6[GitHub #6])。 + +== 版本 1.65.0 + +* 向 `quick_erase` 和 `erase_return_void` 添加弃用属性。 +本次确认将在未来版本中移除这两个接口。 +* 局部标准符合性修正: +** `swap` 自由函数的 `noexcept` 规范。** 补充缺失的 `insert(P&&)` 方法。 + +== 版本 1.64.0 + +* 初步支持 C{plus}{plus}17 新成员函数: +`unordered_map` 中的 `insert_or_assign` 和 `try_emplace`, +* 初步支持 `merge` 与 `extract` 操作。 +目前尚未支持在 `unordered_map` 与 `unordered_multimap` 之间,或 `unordered_set` 与 `unordered_multiset` 之间转移节点。该功能有望在下一版 Boost 中提供。 + +== 版本 1.63.0 + +* 检查 `insert` / `emplace_hint` 操作中提示迭代器的有效性。 +* 修复部分警告(主要出现在测试中)。 +* 为参数数量较少的情况手动编写 `emplace_args` 代码, +以减轻模板错误消息的负担。 +* 移除 emplace 参数中多余的 `boost::forward` 调用, +以修复旧版 Visual C{plus}{plus} 中字面量字符串的就地构造问题。 +* 修复赋值操作中的异常安全问题。若桶分配过程 +抛出异常,可能导致哈希函数与相等性判断函数被覆盖,而现有元素仍保留在容器中。这将造成函数对象与容器元素不匹配,进而引发元素错置桶位及等价元素处理错误的问题。 +* 多项参考文档改进。 +* 增强的分配器支持(https://svn.boost.org/trac/boost/ticket/12459[#12459])。 +* 将无参构造函数改为隐式声明。 +* 实现缺失的分配器感知构造函数。 +* 修复空容器在设置哈希函数与键值相等性判断函数时存在的问题。 +* 从文档示例中移除 unary/binary_function。 +这两者在 C{plus}{plus}17 中已被移除。 +* emplace 方法现支持 10 个构造参数。其设计本应支持最多 10 个参数, +但由于预处理代码中存在差一错误,仅支持 9 个参数。 + +== 版本 1.62.0 + +* 停止使用已弃用的 `boost::iterator` 组件 。 +* 移除 `BOOST_NO_STD_DISTANCE` 的兼容性代码。 +* 移除 `BOOST_UNORDERED_DEPRECATED_EQUALITY` 警告。 +* 采用更简化的赋值操作实现,修复了 +`unordered_multiset` 与 `unordered_multimap` 的异常安全问题,但性能可能略有下降。 +* 停止使用返回值SFINAE技术,以避免部分旧版本编译器的 +兼容性问题。 + +== 版本 1.58.0 + +* 移除常量迭代器中不必要的模板参数。 +* 重命名部分迭代器类中私有的 `iterator` 类型别名,以 +避免某些特征类产生混淆。 +* 修复当使用具有状态且设置了propagate_on_container_move_assign 的分配器时, +移动赋值操作存在的缺陷(https://svn.boost.org/trac/boost/ticket/10777[#10777])。 +* 修复移动赋值操作中一处罕见的异常安全问题。 +* 修复计算待分配桶数量时可能出现的溢出问题 +(https://github.com/boostorg/unordered/pull/4[GitHub #4])。 + +== 版本 1.57.0 + +* 修复迭代器中 `pointer` 类型定义的问题(https://svn.boost.org/trac/boost/ticket/10672[#10672])。 +* 修复 Coverity 警告 +(https://github.com/boostorg/unordered/pull/2[GitHub #2])。 + +== 版本 1.56.0 + +* 修复部分变量遮蔽警告(https://svn.boost.org/trac/boost/ticket/9377[#9377])。 +* 修正文档中的分配器用法(https://svn.boost.org/trac/boost/ticket/9719[#9719])。 +* 对整数键始终采用质数桶数量。此修复解决了 +插入连续整数时的性能回归问题,但可能会降低其他使用场景的速度(https://svn.boost.org/trac/boost/ticket/9282[#9282])。 +* 严格遵循 C{plus}{plus}11 标准规定,仅使用分配器构造元素。 + +== 版本 1.55.0 + +* 避免部分警告(https://svn.boost.org/trac/boost/ticket/8851[#8851] 、 https://svn.boost.org/trac/boost/ticket/8874[#8874])。 +* 避免通过迭代器上的 ADL 暴露部分细节函数。 +* 遵循标准规范,仅使用分配器的 construct 和 destroy +方法来构造和析构存储的元素,不将其用于指针等内部数据的操作。 + +== 版本 1.54.0 + +* 为标准中指定的方法标注 `noexcept` 。更多方法将在下一 +版本中更新。 +* 若已知哈希函数与相等性谓词均具备无抛出 +移动赋值或移动构造特性,则使用它们。 + +== 版本 1.53.0 + +* 移除对旧式变参 pair 构造函数和 +相等性实现的支持,这两项功能自 Boost 1.48 起已被弃用。 +* 停止使用已弃用的配置宏。 +* 更多内部实现变更,包括采用更简化的 +`erase` 方法 实现。 + +== 版本 1.52.0 + +* 加速赋值操作:尽可能复用现有节点进行赋值, +而非创建全新节点并执行复制构造。 +* 修复 `erase_range` 方法中的缺陷(https://svn.boost.org/trac/boost/ticket/7471[#7471])。 +* 回退部分关于节点创建机制的内部变更(尤其 +针对 C{plus}{plus}11 编译器),使 'construct' 和 'destroy' 对 C{plus}{plus}11 分配器更有效。 +* 简化实现,以提高健壮性。 + +== 版本 1.51.0 + +* 修复当使用 C{plus}{plus}11 编译器搭配 C{plus}{plus}03 分配器时 +出现的构造/析构问题(https://svn.boost.org/trac/boost/ticket/7100[#7100])。 +* 移除一段 `try..catch` 代码块以支持无异常编译模式。 +* 调整 SFINAE 的实现方式以兼容 g{plus}{plus} 3.4(https://svn.boost.org/trac/boost/ticket/7175[#7175])。 +* 更新为使用新的配置宏。 + +== 版本 1.50.0 + +* 修复 `unordered_multiset` 与 `unordered_multimap` 的相等性判断逻辑。 +* https://svn.boost.org/trac/boost/ticket/6857[问题单 6857] : +实现 `reserve` 。 +* https://svn.boost.org/trac/boost/ticket/6771[问题 6771] : +规避 gcc 的 `-Wfloat-equal` 编译警告。 +* https://svn.boost.org/trac/boost/ticket/6784[问题单 6784] : +修复部分 Sun 编译器专用代码。 +* https://svn.boost.org/trac/boost/ticket/6190[问题单 6190] : +规避 gcc 的 `-Wshadow` 编译警告。 +* https://svn.boost.org/trac/boost/ticket/6905[问题单 6905] : +使宏中的命名空间与 `bcp` 自定义命名空间兼容(由 Luke Elliott 修复)。 +* 移除部分较小的质数桶数量,因为这些数值 +可能显著增加冲突概率(例如,由于我们使用十进制,5的倍数出现频率很高)。 +* 对于旧版本 Visual C{plus}{plus},优先使用容器库自带的 +`allocator_traits` 实现,因其兼容性更佳。 +* 64 位 std::size_t 机器上使用 2 的幂次桶数量,通过 Thomas +Wang 哈希函数选择桶(因取模运算在 64 位值上极慢)。 +* 部分内部变更。 + +== 版本 1.49.0 + +* 修复因意外出现的异常赋值操作而产生的编译警告。 +* 轻微优化错误消息。 + +== 版本 1.48.0 - 重大更新 + +本本次为重大更新:容器已改用 Boost.Move 的移动操作模拟实现,并显著提升了对 C{plus}{plus}11 标准的符合性。详见 xref:compliance.adoc[标准合规性章节] 。 + +该容器现满足 C{plus}{plus}11 的复杂度要求,但为此会占用稍多内存。这意味着 `quick_erase` 与 `erase_return_void` 接口已不再必需,将在未来版本中移除。 + +C{plus}{plus}11 支持导致部分破坏性变更: + +* 相等性比较已改为遵循 C{plus}{plus}11 规范。 +在包含等价键的容器中,原先要求同一组等价键内的元素必须顺序一致才被视为相等,现在则允许它们互为排列。如需使用旧行为,请定义宏 `BOOST_UNORDERED_DEPRECATED_EQUALITY` 。 + +* 当两个待交换容器的分配器不相等时,swap 操作的行为发生变更。 +旧版本会通过相应的分配器分配新节点,现在则改为:若分配器包含 `propagate_on_container_swap` 成员结构体且其 `propagate_on_container_swap::value` 为 true,则会交换分配器本身。 + +* 现在当调用分配器的 `construct` 与 `destroy` 时,直接传入原始指针, +而非分配器定义的 `pointer` 类型。 + +* `emplace` 方法原先模拟了 +早期 C{plus}{plus}0x 草案中的变参 pair 构造函数。由于该特性已被移除,现不再提供此模拟实现。当前仅模拟新的 `piecewise_construct` pair 构造函数,但需使用 `boost::piecewise_construct` 作为参数。若需启用旧版变参构造函数的模拟行为,请定义宏 `BOOST_UNORDERED_DEPRECATED_PAIR_CONSTRUCT` 。 + +== 版本 1.45.0 + +* 修复了在使用返回 `value_type` 副本的迭代器插入 `unordered_map` 或 `unordered_set` 时的一个错误。 +(使用返回 `value_type` 副本) + +== 版本 1.43.0 + +* https://svn.boost.org/trac/boost/ticket/3966[问题单 3966] : +`erase_return_void` 更名为 `quick_erase` (符合 http://home.roadrunner.com/~hinnant/issue_review/lwg-active.html#579[当前迭代器擦除缓慢的解决方案^])。旧方法名出于向后兼容性予以保留,但已被视为弃用接口,将在未来版本中移除。 +* 使用 Boost.Exception。 +* 停止使用已弃用的 `BOOST_HAS_*` 系列宏。 + +== 版本 1.42.0 + +* 支持使用不完整值类型来实例化容器。 +* 减少编译警告数量(主要出现在测试中)。 +* 提升 Codegear 兼容性。 +* https://svn.boost.org/trac/boost/ticket/3693[问题单 3693] : +新增 `erase_return_void` 作为临时解决方案,以应对当前 `erase` 方法因需查找下一元素返回迭代器而可能产生的效率问题。 +* 新增针对兼容键的模板化 find 重载。 +* https://svn.boost.org/trac/boost/ticket/3773[问题单 3773] : +为 `ptrdiff_t` 添加缺失的 `std` 限定符。 +* 调整代码格式,使大多数代码行不超过 80 字符。 + +== 版本 1.41.0 - 重大更新 + +* 初始版本曾大量使用 +宏来规避旧式编译器薄弱的模板支持能力。鉴于现已不再支持这些编译器,且宏的使用逐渐成为维护负担,现将实现类重构为使用模板替代宏。 + +* 通过 `boost::compressed_pair` 实现 EBO 及微调函数缓冲区 +(现使用 bool 替代成员指针),容器对象的内存占用得以减小。 + +* 桶采用延迟分配机制,这意味着构造空容器时 +不会分配任何内存。 + +== 版本 1.40.0 + +* https://svn.boost.org/trac/boost/ticket/2975[问题单 2975] : +将质数列表存储为预处理序列,确保未来再次调整列表长度时能自动保持正确。 +* https://svn.boost.org/trac/boost/ticket/1978[问题单 1978] : +为所有编译器实现 `emplace` 。 +* https://svn.boost.org/trac/boost/ticket/2908[问题单 2908] 、 +https://svn.boost.org/trac/boost/ticket/3096[问题单 3096] :针对旧版 borland 的变通方案(包括为所有容器添加显式析构函数)。 +* https://svn.boost.org/trac/boost/ticket/3082[问题单 3082] : +禁用 Visual C{plus}{plus} 编译器的错误警告。 +* 对 C{plus}{plus}0x 特性提供优化方案,以应对头文件不可用的情况。 +* 默认创建更少的桶。 + +== 版本 1.39.0 + +* https://svn.boost.org/trac/boost/ticket/2756[问题单 2756] :规避 +Visual C{plus}{plus} 2009 的编译警告。 +* 对实现、测试及文档进行其他少量内部调整。 +(文档) +* 避免 `operator[]` 中不必要的复制。 +* https://svn.boost.org/trac/boost/ticket/2975[问题单 2975] :修正 +质数列表长度。 + +== 版本 1.38.0 + +* 使用 link:../../../../core/swap.html[`boost::swap`] 。 +* https://svn.boost.org/trac/boost/ticket/2237[问题单 2237] : +补充说明:如果两个对象的相等谓词不等价,则它们的相等与不等运算符的行为未定义。感谢 Daniel Krügler。 +* https://svn.boost.org/trac/boost/ticket/1710[问题单 1710]: +采用更长的质数列表。感谢 Thorsten Ottosen 与 Hervé Brönnimann 的贡献。 +* 使用 +link:../../../../type_traits/index.html[对齐存储] 来存储类型。这改变了利用分配器构造节点的方式:原先通过两次调用分配器的 `construct` 方法分别构造指针和值,现在仅通过单次调用构造节点,随后采用就地构造方式构建值对象。 +* 在可用时支持 C{plus}{plus}0x 初始化列表(当前 +仅 g{plus}{plus} 4.4 的 C{plus}{plus}0x 模式)。 + +== 版本 1.37.0 + +* 将带提示的 `emplace` 重载更名为 `emplace++_++hint` 。 +(根据 http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2691.pdf[n2691^] 规范) +* 在 `` 与 +`` 路径下提供前向声明头文件。 +* 将所有实现移至 `boost/unordered` 内,以支持 +模块化并便于跟踪发布版本。 + +== 版本 1.36.0 + +首次正式发布。 + +* 调整内部结构。 +* 移动语义:在支持右值引用的环境中提供完整支持,否则 +使用精简版 Adobe 移动库进行模拟实现。 +* 支持右值引用和变参模板时提供就地构造支持。 +* 节点分配效率更高(当右值引用和变参模板 +可用时)。 +* 新增相等性判断运算符。 + +== Boost 1.35.0 附加版本 - 2008年3月31日 + +非官方版本已上传至资源库,适用于 Boost 1.35.0。该版本已采纳评审阶段的多项改进建议。 + +* 通过 Boost 回归测试,代码可移植性得到显著提升。 +* 修正文档中的多处笔误,并优化文本表述以提升可读性。 +* 修复根据最大负载因子计算容器大小时,浮点数到 `std::size_t` 的转换问题, +并在计算过程中使用 `double` 类型来提高精度。 +* 修正示例中的部分错误。 + +== 评审版本 + +初始评审版本(评审时间:2007年12月7日至12月16日)。 diff --git a/doc/modules/ROOT/pages/compliance_zh_Hans.adoc b/doc/modules/ROOT/pages/compliance_zh_Hans.adoc new file mode 100644 index 0000000..5d1331b --- /dev/null +++ b/doc/modules/ROOT/pages/compliance_zh_Hans.adoc @@ -0,0 +1,94 @@ +[#compliance] += 标准符合性 + +:idprefix: compliance_ + +:cpp: C++ + +== 闭寻址容器 + +`boost::unordered_[multi]set` 和 `boost::unordered_[multi]map` 为支持 {cpp}11 或更高版本的编译器提供了符合最新标准修订版中 {cpp} 无序关联容器规范的实现,仅有极少量的已知偏差。这些容器完全遵循 https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[分配器感知^] ,并支持 https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[花式指针^]。 + +=== 推导指引 + +https://en.cppreference.com/w/cpp/language/class_template_argument_deduction[类模板参数推导 (CTAD)] 的推导指南仅在 C{plus}{plus}17(或更高版本)编译器中可用。 + +=== 分段式 Pair 就地构造 + +根据标准规范, `boost::unordered_[multi]map::emplace` 支持 pair 的分段构造: + +[source,c++] +---- +boost::unordered_multimap x; + +x.emplace( + std::piecewise_construct, + std::make_tuple("key"), std::make_tuple(1, 2)); +---- + +此外,通过非标准接口 `boost::unordered::piecewise_construct` 与 Boost.Tuple 提供相同功能: + +[source,c++] +---- +x.emplace( + boost::unordered::piecewise_construct, + boost::make_tuple("key"), boost::make_tuple(1, 2)); +---- + +此特性为保持与 Boost.Unordered 旧版本的向后兼容性而保留:建议用户更新代码以使用 `std::piecewise_construct` 与 `std::tuple` 。 + +=== 交换 + +执行交换操作时,当前未通过调用 `swap` 函数来交换 `Pred` 和 `Hash` 对象,而是使用其复制构造函数。因此,在交换过程中可能会因其复制构造函数而抛出异常。 + +== 开放寻址容器 + +C++ 标准目前并未提供任何可供遵循的开放定址容器规范,因此 `boost::unordered_flat_set` / `unordered_node_set` 与 `boost::unordered_flat_map` / `unordered_node_map` 分别借鉴 `std::unordered_set` 和 `std::unordered_map` 的设计,并在其内部数据结构(与标准规定的闭寻址方式截然不同)允许或需要时,对接口进行相应调整。 + +Boost.Unordered 提供的开放寻址容器仅兼容符合 C{plus}{plus}1(或更高版本)的编译器,且不再对移动语义、变参模板等语言特性进行模拟实现。这些容器完全符合 https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[分配器感知] 规范,并支持 https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[花式指针] 。 + + +与 C{plus}{plus} 无序关联容器的主要区别在于: + +* 总体特性: +** `begin()` 不是常数时间复杂度操作。 ** `erase(iterator)` 不返回指向下一元素的迭代器,而是返回一个代理对象,该对象可按需转换为目标迭代器;这可以避免在无需迭代器时可能产生的高昂递增操作开销。 ** 未提供用于桶管理的 API(除 `bucket_count` 外)。 ** 容器的最大负载因子由内部自动管理,用户无法手动设置。通过公开函数 `max_load` 获取的最大负载值,在高负载情况下执行删除操作时可能会降低。 +* 扁平容器( `boost::unordered_flat_set` 与 `boost::unordered_flat_map` ): +** `value_type` 必须支持移动构造。 ** 在重哈希的过程中,指针稳定性无法保持。 ** 不提供节点提取/插入的 API 接口。 + +== 并发容器 + +目前 C++ 标准中尚未针对此类或任何其他类型的并发数据结构提供规范。`boost::concurrent_flat_set` / `boost::concurrent_node_set` 与 `boost::concurrent_flat_map` / `boost::concurrent_node_map` 的 API 分别参照 `std::unordered_flat_set` 和 `std::unordered_flat_map` 设计,但有一个关键区别:由于迭代器在并发场景中存在的固有问题(高竞争、易死锁),这些容器不提供迭代器。因此,Boost.Unordered 并发容器在技术上并不符合 https://en.cppreference.com/w/cpp/named_req/Container[容器^] 的模型,但它们满足 https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[分配器感知^] 容器(包括 https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[花式指针^] 支持)中除涉及迭代器之外的所有要求。 + +在非并发无序容器中,迭代器主要有两项核心功能: + +* 访问先前通过查找定位的元素。 +* 容器遍历。 + +为替代迭代器,Boost.Unordered 并发容器使用__内部访问__机制作为线程安全的替代方案。传统操作会返回指向容器中既有元素的迭代器(例如下列): + +[source,c++] +---- +iterator find(const key_type& k); +std::pair insert(const value_type& obj); +---- + +被改造为接受传递该元素的__访问函数__: + +[source,c++] +---- +template size_t visit(const key_type& k, F f); +template bool insert_or_visit(const value_type& obj, F f); +---- + +(第二种情况中,仅当表中存在与 `obj` 等价元素时才会调用 `f` ,而非在插入成功时调用)。容器遍历通过以下方式实现: + +[source,c++] +---- +template size_t visit_all(F f); +---- + +在支持并行算法的 C{plus}{plus}17 编译器中提供并行化版本。通常,并发容器的接口通过将迭代器替换为访问机制的过程从非并发版本派生。对于常规映射, `iterator` 和 `const_iterator` 分别提供对元素的非常量和常量访问;此处访问权限取决于所用成员函数的常量性(同时提供 `*cvisit` 重载用于显式常量访问)。对于 `boost::concurrent_flat_set` ,访问操作始终为常量。 + +`boost::concurrent_flat_map` / `boost::concurrent_node_map` 未提供的一个关键操作是 `operator[]` / `at` ,该功能可通过 xref:reference/concurrent_flat_map.adoc#concurrent_flat_map_try_emplace_or_cvisit[`try_emplace_or_visit`] 实现替代(该方式更为复杂)。 + +//- diff --git a/doc/modules/ROOT/pages/concurrent_zh_Hans.adoc b/doc/modules/ROOT/pages/concurrent_zh_Hans.adoc new file mode 100644 index 0000000..e77b0bb --- /dev/null +++ b/doc/modules/ROOT/pages/concurrent_zh_Hans.adoc @@ -0,0 +1,251 @@ += 并发容器 + +:idprefix: concurrent_ + +Boost.Unordered 提供 `boost::concurrent_node_set` 、 `boost::concurrent_node_map` 、 `boost::concurrent_flat_set` 和 `boost::concurrent_flat_map` ,这些哈希表允许不同线程进行并发读写访问,无需用户实现任何同步机制。 + +[source,c++] +---- +std::vector input; +boost::concurrent_flat_map m; + +... + +// process input in parallel +const int num_threads = 8; +std::vector threads; +std::size_t chunk = input.size() / num_threads; // how many elements per thread + +for (int i = 0; i < num_threads; ++i) { + threads.emplace_back([&,i] { + // calculate the portion of input this thread takes care of + std::size_t start = i * chunk; + std::size_t end = (i == num_threads - 1)? input.size(): (i + 1) * chunk; + + for (std::size_t n = start; n < end; ++n) { + m.emplace(input[n], calculation(input[n])); + } + }); +} +---- + +在上例中,线程无需同步即可访问 `m` ,这与单线程场景中的操作方式一致。在理想情况下,若将给定工作负载分配给 _N_ 个线程,其执行速度相较单线程提升 _N_ 倍——由于同步开销与__争用__的存在(某线程等待其他线程离开容器的锁定区域),实践中无法达到此理论极限。但 Boost.Unordered 并发容器设计为以极低开销运行,通常可实现__线性扩展__(即性能提升与线程数量成正比,直至达到 CPU 的逻辑核心数)。 + +== 基于访问的 API + +Boost.Unordered 并发容器的新用户首先会注意到:这些类__不提供迭代器__(它们在技术层面上不符合 C{plus}{plus} 标准中的 https://en.cppreference.com/w/cpp/named_req/Container[容器] 定义)。因为迭代器本质上是线程不安全的。请参考以下假设代码: + +[source,c++] +---- +auto it = m.find(k); // A: get an iterator pointing to the element with key k +if (it != m.end() ) { + some_function(*it); // B: use the value of the element +} +---- + +多线程场景中,若其他线程在 A 和 B 之间执行 `m.erase(k)` 操作,迭代器 `it` 可能在 B 点失效。虽然存在通过锁定指向元素来修复此问题的设计方案,但这种方法容易引发高竞争并可能导致程序死锁。 `operator++[]++` 也存在类似并发问题,因此 `boost::concurrent++_++flat++_++map` / `boost::concurrent++_++node++_++map` 也未提供该操作。替代方案是通过__访问函数__操作元素: + +[source,c++] +---- +m.visit(k, [](const auto& x) { // x is the element with key k (if it exists) + some_function(x); // use it +}); +---- + +用户传递的访问函数(此处为 lambda 函数)由 Boost.Unordered 在内部以线程安全的方式执行,因此该函数可以安全访问目标元素,无需担心其他线程在此过程中造成干扰。 + +另一方面,访问函数__无法__访问容器本身: + +[source,c++] +---- +m.visit(k, [&](const auto& x) { + some_function(x, m.size()); // forbidden: m can't be accessed inside visitation +}); +---- + +但允许访问其他容器: + +[source,c++] +---- +m.visit(k, [&](const auto& x) { + if (some_function(x)) { + m2.insert(x); // OK, m2 is a different boost::concurrent_flat_map + } +}); +---- + +但通常而言,访问函数应尽可能轻量以减少争用并提升并行性。在某些情况下,将繁重操作移出访问函数可能更优: + +[source,c++] +---- +std::optional o; +bool found = m.visit(k, [&](const auto& x) { + o = x; +}); +if (found) { + some_heavy_duty_function(*o); +} +---- + +访问机制在并发容器 API 中占据核心地位,许多经典操作都提供了支持访问机制的变体: + +[source,c++] +---- +m.insert_or_visit(x, [](auto& y) { + // if insertion failed because of an equivalent element y, + // do something with it, for instance: + ++y.second; // increment the mapped part of the element +}); +---- + +注意此例中访问函数可__修改__元素:作为通用规则,对并发映射 `m` 的操作将根据 `m` 是否为常量类型,授予访问函数对元素的常量/非常量访问权限。 通过使用 `cvisit` 重载(如 `insert++_++or++_++cvisit` )可始终显式请求常量访问,这可能提升并行性。而并发集合的访问始终为常量访问。 + +尽管预期使用频率较低,并发容器还提供在元素创建后立即进行访问的插入操作(除在已存在等价元素时执行常规访问之外): + +[source,c++] +---- + m.insert_and_cvisit(x, + [](const auto& y) { + std::cout<< "(" << y.first << ", " << y.second <<") inserted\n"; + }, + [](const auto& y) { + std::cout<< "(" << y.first << ", " << y.second << ") already exists\n"; + }); +---- + +支持访问功能的完整操作列表请参阅 xref:reference/concurrent_node_set.adoc#concurrent_node_set[`boost::concurrent++_++node++_++set`] 、 xref:reference/concurrent_node_map.adoc#concurrent_node_map[`boost::concurrent++_++node++_++map`] 、 xref:reference/concurrent_flat_set.adoc#concurrent_flat_set[`boost::concurrent++_++flat++_++set`] 和 xref:reference/concurrent_flat_map.adoc#concurrent_flat_map[`boost::concurrent++_++flat++_++map`] 的参考文档。 + +== 全表访问 + +在缺乏迭代器的情况下, `visit++_++all` 提供处理容器内全部元素的替代方案: + +[source,c++] +---- +m.visit_all([](auto& x) { + x.second = 0; // reset the mapped part of the element +}); +---- + +在支持标准并行算法的 C{plus}{plus}17 编译器中,全表遍历访问可进行并行化处理: + +[source,c++] +---- +m.visit_all(std::execution::par, [](auto& x) { // run in parallel + x.second = 0; // reset the mapped part of the element +}); +---- + +遍历过程可中途中断: + +[source,c++] +---- +// finds the key to a given (unique) value + +int key = 0; +int value = ...; +bool found = !m.visit_while([&](const auto& x) { + if(x.second == value) { + key = x.first; + return false; // finish + } + else { + return true; // keep on visiting + } +}); + +if(found) { ... } +---- + +最后一项全表访问操作是 `erase++_++if` : + +[source,c++] +---- +m.erase_if([](auto& x) { + return x.second == 0; // erase the elements whose mapped value is zero +}); +---- + +`visit++_++while` 与 `erase++_++if` 同样支持并行化。需要注意的是,为提升执行效率,全表遍历操作在运行期间不会锁定整个容器:这意味着在遍历过程中,其他线程可能同时执行元素的插入、修改或删除操作。建议在程序中避免对并发容器在任意时间点的全局状态做过强的假设。 + +== 批量访问 + +假设有一个 `std::array` 存储了需要在并发映射中查找的键: + +[source,c++] +---- +std::array keys; +... +for(const auto& key: keys) { + m.visit(key, [](auto& x) { ++x.second; }); +} +---- + +__批量访问__允许用户通过单次操作传入所有键: + +[source,c++] +---- +m.visit(keys.begin(), keys.end(), [](auto& x) { ++x.second; }); +---- + +提供此功能并非仅出于语法便利性考量:通过一次性处理所有键,可应用内部优化提升性能(详见 xref:benchmarks.adoc#benchmarks_boostconcurrent_flatnode_map[基准测试])。实际上,用户可通过缓冲输入键值以实现分块批量访问,从而进一步提升效率: + +[source,c++] +---- +static constexpr auto bulk_visit_size = boost::concurrent_flat_map::bulk_visit_size; +std::array buffer; +std::size_t i=0; +while(...) { // processing loop + ... + buffer[i++] = k; + if(i == bulk_visit_size) { + map.visit(buffer.begin(), buffer.end(), [](auto& x) { ++x.second; }); + i = 0; + } + ... +} +// flush remaining keys +map.visit(buffer.begin(), buffer.begin() + i, [](auto& x) { ++x.second; }); +---- + +这里存在延迟与吞吐量的权衡:缓冲机制会延长单个键的处理延迟,但每秒处理的键总数(吞吐量)会更高。 `bulk++_++visit++_++size` 是推荐的缓冲区块大小——过小的缓冲区可能导致性能下降。 + +== 阻塞式操作 + +并发容器可像其他 Boost.Unordered 容器一样支持复制、赋值、清空和合并操作。与大多数其他操作不同,这些属于__阻塞式操作__:在执行期间,其他线程将被阻止访问相关容器。该阻塞机制由库自动处理,用户无需采取特殊预防措施,但整体性能可能受到影响。 + +另一阻塞操作是__重哈希__,该操作可通过 `rehash` / `reserve` 显式触发,或在插入过程中当容器负载达到 `max++_++load()` 时自动执行。与非并发容器类似,在批量插入前预先分配空间通常能加速处理过程。 + +== 与非并发容器的互操作性 + +由于开放寻址容器与并发容器基于相同的内部数据结构,它们可以高效地通过移动构造从其非并发对应容器转换而来,反之亦然。 + +[caption=, title='Table {counter:table-counter}. Concurrent/non-concurrent interoperatibility'] +[cols="1,1", frame=all, grid=all] +|=== +^|`boost::concurrent_node_set` ^|`boost::unordered_node_set` + +^|`boost::concurrent_node_map` ^|`boost::unordered_node_map` + +^|`boost::concurrent_flat_set` ^|`boost::unordered_flat_set` + +^|`boost::concurrent_flat_set` ^|`boost::unordered_flat_set` + +|=== + +此互操作性适用于多阶段场景:部分数据处理环节需要并行执行,而其他步骤采用非并发(或只读)模式。下例中,我们需要从一个庞大的单词输入向量构建词频统计直方图:填充阶段用 `boost::concurrent++_++flat++_++map` 并行执行,随后将结果转移至最终容器。 + +[source,c++] +---- +std::vector words = ...; + +// Insert words in parallel +boost::concurrent_flat_map m0; +std::for_each( + std::execution::par, words.begin(), words.end(), + [&](const auto& word) { + m0.try_emplace_or_visit(word, 1, [](auto& x) { ++x.second; }); + }); + +// Transfer to a regular unordered_flat_map +boost::unordered_flat_map m=std::move(m0); +---- diff --git a/doc/modules/ROOT/pages/copyright_zh_Hans.adoc b/doc/modules/ROOT/pages/copyright_zh_Hans.adoc new file mode 100644 index 0000000..527cc80 --- /dev/null +++ b/doc/modules/ROOT/pages/copyright_zh_Hans.adoc @@ -0,0 +1,20 @@ +[#copyright] += 版权与许可协议 + +:idprefix: copyright_ + +*丹尼尔·詹姆斯 (Daniel James)* + +版权所有 © 2003, 2004 杰里米·B·梅廷-谢泼德 (Jeremy B. Maitin-Shepard) + +版权所有 © 2005-2008 丹尼尔·詹姆斯(Daniel James) + +版权所有 © 2022-2025 克里斯蒂安·马扎卡斯 (Christian Mazakas) + +版权所有 © 2022-2025 华金·M·洛佩斯·穆尼奥斯(Joaquín M López Muñoz) + +版权所有 © 2022-2023 彼得·迪莫夫 (Peter Dimov) + +版权所有 © 2024 布雷登·加涅茨基 (Braden Ganetsky) + +根据Boost软件许可证1.0版分发。(详见随附文件 LICENSE++_++1++_++0.txt 或访问 http://www.boost.org/LICENSE_1_0.txt 。) diff --git a/doc/modules/ROOT/pages/debuggability_zh_Hans.adoc b/doc/modules/ROOT/pages/debuggability_zh_Hans.adoc new file mode 100644 index 0000000..4ab6146 --- /dev/null +++ b/doc/modules/ROOT/pages/debuggability_zh_Hans.adoc @@ -0,0 +1,64 @@ +[#debuggability] +:idprefix: debuggability_ + += 可调试性 + +== 调试可视化 + +所有容器和迭代器在 Natvis 框架中均具备自定义可视化功能。 + +=== 在项目中使用 + +若要在项目中通过 Natvis 框架实现 Boost.Unordered 容器的可视化调试,只需将文件 link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered.natvis[/extra/boost++_++unordered.natvis] 作为"现有项"添加到 Visual Studio 项目中即可。 + +=== 可视化结构 + +可视化方案与标准无序容器保持一致。容器单次最多显示 100 个元素。集合类容器中每个元素的项名称显示为 `++[++i++]++` (其中 `i` 为从 `0` 开始的显示索引)。映射元素默认显示为 `++[{++ 键值显示}++]++` ,例如当首元素为键值对 `("abc", 1)` 时,其项名称将显示为 `++[++"abc"++]++` 。用户可通过启用"ShowElementsByIndex"视图模式切换为按索引命名元素,此视图命名规则与标准无序容器保持一致。 + +默认情况下,封闭寻址容器将显示 `++[++hash++_++function++]++` 、 `++[++key++_++eq++]++` 、适用的 `++[++spare++_++hash++_++function++]++` 与 `++[++spare++_++key++_++eq++]++` 、 `++[++allocator++]++` 以及元素列表。若启用"detailed"视图将额外显示 `++[++bucket++_++count++]++` 和 `++[++max++_++load++_++factor++]++` 信息;若启用"simple"视图,则仅显示元素列表而不包含其他条目。 + +默认情况下,开放寻址容器显示 `++[++hash++_++function++]++` 、 `++[++key++_++eq++]++` 、 `++[++allocator++]++` 以及元素列表。若启用 "simple" 视图,则仅显示元素列表而不包含其他条目。无论采用 SIMD 还是非 SIMD 实现,均可通过 Natvis 框架进行可视化调试。 + +迭代器显示方式与标准迭代器类似:指向元素的迭代器显示为元素值;结束迭代器显示为 `++{++ 结束迭代器 }` 。 + +=== 花式指针 + +分配器中使用花式指针(如 `boost::interprocess::offset++_++ptr` )时,容器可视化功能仍可正常运作。Boost.Unordered 通过 Natvis 定制点支持所有类型的花式指针。 `boost::interprocess::offset++_++ptr` 已在 Boost.Interprocess 库中预定义支持,自定义类型支持方法请参阅文件 link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered.natvis[/extra/boost++_++unordered.natvis] 末尾注释。 + +== GDB 美化打印器 + +所有容器和迭代器均配备自定义 GDB 美化打印器。 + +=== 在项目中使用 + +使用美化打印器时,必须始终按以下方式启用美化打印功能。此项配置通常只需执行一次。 + +```plaintext + +(gdb) set print pretty on + +``` + +默认情况下,若编译目标为 ELF 二进制格式,生成的文件将内置 Boost.Unordered 美化打印器。要使用内嵌的美化打印器,请确保按以下方式启用自动加载功能(此操作需在每次启动 GDB 时执行,或将其添加到 ".gdbinit" 配置文件中)。 + +```plaintext + +(gdb) add-auto-load-safe-path [/path/to/executable] + +=== 可视化结构 + +可视化方案与标准无序容器保持一致:映射容器显示键到映射值的关联;集合容器显示从索引到值的关联关系。迭代器可显示为指向的元素内容,或标记为结束迭代器。下图展示了示例 `boost::unordered++_++map` 、示例 `boost::unordered++_++set` 及其迭代器. + +```plaintext + +(gdb) print example_unordered_map $1 = boost::unordered_map with 3 elements = {["C"] = "c", ["B"] = "b", ["A"] = "a"} (gdb) print example_unordered_map_begin $2 = iterator = { {first = "C", second = "c"} } (gdb) print example_unordered_map_end $3 = iterator = { end iterator } (gdb) print example_unordered_set $4 = boost::unordered_set with 3 elements = {[0] = "c", [1] = "b", [2] = "a"} (gdb) print example_unordered_set_begin $5 = iterator = { "c" } (gdb) print example_unordered_set_end $6 = iterator = { end iterator } + +``` + +其余容器的显示逻辑完全一致,仅在显示容器本身时将其模板名称替换为对应的 "boost::unordered++_{++map++|++set}"。需要注意的事,每个子元素(即键、映射值或值)的显示均取决于其自身的打印设置,这可能包含其专属的美化打印器。 + +无论采用 SIMD 还是非 SIMD 实现,均可通过 GDB 美化打印器查看。 + +=== 花式指针 + +当用户在分配器中使用花式指针(例如 `boost::interprocess::offset++_++ptr` )时,GDB 的美化打印器仍可正常工作。虽然这种情况不常见,但 Boost.Unordered 为美化打印器提供了定制点,以支持任何类型的花式指针。 `boost::interprocess::offset++_++ptr` 的支持已在Boost.Interprocess库中预先定义,自定义类型支持方法请参阅文件 link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered_printers.py[/extra/boost++_++unordered++_++printers.py] 的末尾注释。 diff --git a/doc/modules/ROOT/pages/hash_equality_zh_Hans.adoc b/doc/modules/ROOT/pages/hash_equality_zh_Hans.adoc new file mode 100644 index 0000000..c15a31c --- /dev/null +++ b/doc/modules/ROOT/pages/hash_equality_zh_Hans.adoc @@ -0,0 +1,70 @@ +[#hash_equality] + +:idprefix: hash_equality_ + += 相等性谓词与哈希函数 + +关联容器使用排序关系来规定元素的存储方式,而无序关联容器则依赖相等性谓词与哈希函数。例如, `xref:reference/unordered_map.adoc[boost::unordered_map]`的声明如下: + +```cpp template < class Key, class Mapped, class Hash = boost::hash, class Pred = std::equal_to, class Alloc = std::allocator > > class unordered_map; ``` + +哈希函数放在首位,因为你可能只想更改哈希函数而不想更改相等谓词。例如,如果你想使用 https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash[FNV-1a 哈希^],可以这样写: + +```cpp boost::unordered_map dictionary; ``` + +examples 目录中有一个链接:../../../examples/fnv1.hpp[implementation of FNV-1a^] + +```cpp + +```cpp struct iequal_to { bool operator()(std::string const& x, std::string const& y) const { return boost::algorithm::iequals(x, y, std::locale()); } }; + +struct ihash { std::size_t operator()(std::string const& x) const { std::size_t seed = 0; std::locale locale; + +for(std::string::const_iterator it = x.begin(); it != x.end(); ++it) { boost::hash_combine(seed, std::toupper(*it, locale)); } + +return seed; } }; ``` + +然后你可以在不区分大小写的字典中使用它:```cpp boost::unordered_map idictionary; ``` + +这是 link:../../../examples/case_insensitive.hpp[/libs/unordered/examples/case_insensitive.hpp^] 处示例的简化版本,该示例支持其他区域设置和字符串类型。 + +CAUTION: 在使用自定义相等谓词时,请谨慎使用相等(==)运算符 +在使用自定义相等谓词(特别是函数指针)时请务必小心。如果你用不同的相等谓词比较两个容器,结果是未定义的。对于大多数无状态函数对象而言,这种情况不可能发生——因为你只能使用相同的相等谓词来比较对象,所以两个相等谓词必然相等。但如果你使用的是函数指针或有状态的相等谓词(例如 boost::function),则可能会遇到问题。 + +== 自定义类型 + +类似地,自定义类型也可使用自定义哈希函数: + +```cpp struct point { int x; int y; }; + +bool operator==(point const& p1, point const& p2) { return p1.x == p2.x && p1.y == p2.y; } + +struct point_hash { std::size_t operator()(point const& p) const { std::size_t seed = 0; boost::hash_combine(seed, p.x); boost::hash_combine(seed, p.y); return seed; } }; + +boost::unordered_multiset points; ``` + +由于默认哈希函数是 link:../../../../container_hash/index.html[Boost.Hash^],我们可以扩展它以支持该类型,从而无需显式给出哈希函数: + +```cpp struct point { int x; int y; }; + +bool operator==(point const& p1, point const& p2) { return p1.x == p2.x && p1.y == p2.y; } + +std::size_t hash_value(point const& p) { std::size_t seed = 0; boost::hash_combine(seed, p.x); boost::hash_combine(seed, p.y); return seed; } + +// Now the default function objects work. +boost::unordered_multiset points; ``` + +有关如何执行此操作的更多详细信息,请参阅 link:../../../../container_hash/index.html[Boost.Hash 文档^]。请记住,它依赖于标准扩展—因此它不适用于无序关联容器的其他实现,你需要显式使用 Boost.Hash。 + +[caption=, title='Table {counter:table-counter} Methods for accessing the hash and equality functions'] +[cols="1,.^1", frame=all, grid=rows] +|=== +|Method |Description + +|`hasher hash_function() const` +|Returns the container's hash function. + +|`key_equal key_eq() const` +|Returns the container's key equality function.. + +|=== diff --git a/doc/modules/ROOT/pages/hash_quality_zh_Hans.adoc b/doc/modules/ROOT/pages/hash_quality_zh_Hans.adoc new file mode 100644 index 0000000..bc0648d --- /dev/null +++ b/doc/modules/ROOT/pages/hash_quality_zh_Hans.adoc @@ -0,0 +1,114 @@ +[#hash_quality] = 哈希质量 + +:idprefix: hash_quality_ + +为了正常工作,哈希表要求提供的哈希函数具有__高质量__,这大致意味着它应尽可能均匀地使用其 `std::size++_++t` 输出空间,就像随机数生成器那样——当然,不同之处在于哈希函数的值并非随机,而是由其输入参数严格决定。 + +Boost.Unordered 中的闭寻址容器对质量不理想的哈希函数具有较好的鲁棒性,但开放寻址和并发容器对此因素更为敏感。如果哈希函数选择不当,其性能会出现显著下降。通常,使用由 link:../../../../container_hash/index.html[Boost.Hash] 提供或生成的函数可确保质量达标,但在使用其他哈希算法时则需要特别谨慎。 + +本节剩余的内容仅适用于开放寻址容器与并发容器。 + +== 哈希后混合处理与雪崩效应属性 + +即使提供的哈希函数不符合开放寻址所需的均匀分布特性,Boost.Unordered容器的性能通常仍可接受,这是因为库会执行内部__后混合处理__步骤来改善计算哈希值的统计特性。当然这会带来额外的计算开销;若希望禁用后混合处理功能,请按以下方式对哈希函数进行注解: + +[source,c++] +---- +struct my_string_hash_function +{ + using is_avalanching = std::true_type; // instruct Boost.Unordered to not use post-mixing + + std::size_t operator()(const std::string& x) const + { + ... + } +}; +---- + +通过设置 link:../../../../container_hash/doc/html/hash.html#ref_hash_is_avalanchinghash[`hash++_++is++_++avalanching`] 特征,我们告知Boost.Unordered: `my++_++string++_++hash++_++function` 具有足够高的质量,无需任何后混合处理安全网即可直接使用。但这样做的风险在于,如果哈希函数的表现未达到我们声明的理想状态,则可能导致性能下降。 + +== 容器统计信息 + +若在全局定义宏 `BOOST++_++UNORDERED++_++ENABLE++_++STATS` ,开放寻址容器与并发容器将计算与哈希函数质量直接相关的内部统计信息: + +[source,c++] +---- +#define BOOST_UNORDERED_ENABLE_STATS +#include + +... + +int main() +{ + boost::unordered_flat_map m; + ... // use m + + auto stats = m.get_stats(); + ... // inspect stats +} +---- + +`stats` 对象提供以下统计信息: + +[source,subs=+quotes] +---- +stats + .insertion // *Insertion operations* + .count // Number of operations + .probe_length // Probe length per operation + .average + .variance + .deviation + .successful_lookup // *Lookup operations (element found)* + .count // Number of operations + .probe_length // Probe length per operation + .average + .variance + .deviation + .num_comparisons // Elements compared per operation + .average + .variance + .deviation + .unsuccessful_lookup // *Lookup operations (element not found)* + .count // Number of operations + .probe_length // Probe length per operation + .average + .variance + .deviation + .num_comparisons // Elements compared per operation + .average + .variance + .deviation +---- + +系统维护三类内部操作的统计信息:插入操作(不考虑先前查找键是否存在的操作)、成功查找及未命中查找(包括插入元素时触发的内部查询)。__探测长度__是指每次操作所访问的 xref:structures.adoc#structures_open_addressing_containers[桶组] 数量。若哈希函数表现正常: + +* 平均探测长度应接近1.0。 +* 每次成功查找的平均比较次数应接近 1.0(即, +仅检查找到的那个元素)。 +* 每次失败查找的平均比较次数应接近 0.0。 + +提供了一个链接:../../../benchmark/string_stats.cpp[示例^],用于展示 `boost::hash`、https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash[FNV-1a 哈希^] 以及两种行为不当但被错误标记为具有雪崩效应的自定义哈希函数的容器统计信息。 + +[listing] +---- + boost::unordered_flat_map: 319 ms + insertion: probe length 1.08771 + successful lookup: probe length 1.06206, num comparisons 1.02121 + unsuccessful lookup: probe length 1.12301, num comparisons 0.0388251 + + boost::unordered_flat_map, FNV-1a: 301 ms + insertion: probe length 1.09567 + successful lookup: probe length 1.06202, num comparisons 1.0227 + unsuccessful lookup: probe length 1.12195, num comparisons 0.040527 + +boost::unordered_flat_map, slightly_bad_hash: 654 ms + insertion: probe length 1.03443 + successful lookup: probe length 1.04137, num comparisons 6.22152 + unsuccessful lookup: probe length 1.29334, num comparisons 11.0335 + + boost::unordered_flat_map, bad_hash: 12216 ms + insertion: probe length 699.218 + successful lookup: probe length 590.183, num comparisons 43.4886 + unsuccessful lookup: probe length 1361.65, num comparisons 75.238 +---- diff --git a/doc/modules/ROOT/pages/intro_zh_Hans.adoc b/doc/modules/ROOT/pages/intro_zh_Hans.adoc new file mode 100644 index 0000000..13be990 --- /dev/null +++ b/doc/modules/ROOT/pages/intro_zh_Hans.adoc @@ -0,0 +1,57 @@ +[#intro] += 引言 + +:idprefix: intro_ :cpp: C++ + +https://en.wikipedia.org/wiki/Hash_table[哈希表] 是一种极其流行的计算机数据结构,几乎在所有编程语言中都能找到其不同形式的存在。红黑树(在 C{plus}{plus} 中被 `std::set` 和 `std::map` 使用)等其他关联结构在插入和查找操作上具有对数时间复杂度,但哈希表若配置得当,这些操作的平均时间复杂度可达到常数级别,并且通常速度更快。 + +C{plus}{plus}11 标准引入了__无序关联容器__ `std::unordered++_++set` 、 `std::unordered++_++map` 、 `std::unordered++_++multiset` 和 `std::unordered++_++multimap` ,但针对哈希表的研究并未止步:CPU架构的进步(如更强大的缓存、 https://en.wikipedia.org/wiki/Single_instruction,_multiple_data[SIMD] 指令集以及日益普及的 https://en.wikipedia.org/wiki/Multi-core_processor[多核处理器])为改进基于哈希的数据结构创造了新的可能,这些新应用场景已远超 2011 年标准所定义的无序关联容器的能力范围。 + +Boost.Unordered 提供了一系列哈希容器,它们在标准符合性、性能表现和设计应用场景上各有不同: + +[caption=, title='Table {counter:table-counter}. Boost.Unordered containers'] +[cols="1,1,.^1", frame=all, grid=all] +|=== +^h| ^h|*基于节点* ^h|*扁平* + +^.^h|*Closed addressing* ^m| boost::unordered_set + boost::unordered_map + boost::unordered_multiset + boost::unordered_multimap ^| + +^.^h|*开放寻址法* ^m| boost::unordered_node_set + boost::unordered_node_map ^m| boost::unordered_flat_set + boost::unordered_flat_map + +^.^h|*并发* ^| `boost::concurrent_node_set` + `boost::concurrent_node_map` ^| `boost::concurrent_flat_set` + `boost::concurrent_flat_map` + +|=== + +* **闭寻址容器**完全符合C{plus}{plus}无序关联容器规范, +并在标准接口的技术约束范围内提供了业界领先的运行性能。 +* **开放寻址容器**采用了更高效的数据结构与算法 +(典型场景下性能提升2倍以上),同时为了适配实现方案而对标准接口做了细微调整。该类容器包含两种变体:*扁平式*(最快)与**基于节点式**,后者在重新哈希时能保持指针稳定性,但性能会有所下降。 +* 最后,**并发容器**专为高性能多线程场景设计与实现, +其接口与常规C{plus}{plus}容器存在根本性差异。该系列同时提供扁平与基于节点两种变体。 + +Boost.Unordered 中的所有 set 和 map 分别与 `std::unordered_set` 和 `std::unordered_map` 类似的方式进行实例化: + +[source,c++] +---- +namespace boost { + template < + class Key, + class Hash = boost::hash, + class Pred = std::equal_to, + class Alloc = std::allocator > + class unordered_set; + // same for unordered_multiset, unordered_flat_set, unordered_node_set, + // concurrent_flat_set and concurrent_node_set + + template < + class Key, class Mapped, + class Hash = boost::hash, + class Pred = std::equal_to, + class Alloc = std::allocator > > + class unordered_map; + // same for unordered_multimap, unordered_flat_map, unordered_node_map, + // concurrent_flat_map and concurrent_node_map +} +---- + +在无序关联容器中存储对象需要同时提供键相等函数和哈希函数。标准容器中的默认函数对象支持若干基本类型,包括整数类型、浮点类型、指针类型以及标准字符串。由于 Boost.Unordered 使用 link:../../../../container_hash/index.html[boost::hash^],它还支持其他一些类型,包括标准容器。要使用这些方法不支持的任意类型,用户必须扩展 Boost.Hash 以支持该类型,或者使用自定义的相等谓词和哈希函数。更多详情请参见 xref:hash_equality.adoc#hash_equality[相等谓词与哈希函数] 一节。 diff --git a/doc/modules/ROOT/pages/rationale_zh_Hans.adoc b/doc/modules/ROOT/pages/rationale_zh_Hans.adoc new file mode 100644 index 0000000..4f8f5d5 --- /dev/null +++ b/doc/modules/ROOT/pages/rationale_zh_Hans.adoc @@ -0,0 +1,79 @@ +[#rationale] + +:idprefix: rationale_ + += 实现设计依据 + +== 闭寻址容器 + +`boost::unordered++_[++multi++]++set` 与 `boost::unordered++_[++multi++]++map` 遵循无序关联容器的标准规范,因此其接口是确定的。但在实现层面仍需做出一些决策,其首要考量是标准符合性与跨平台移植性。 + +关于哈希表的通用实现问题的详细综述,可参阅 http://en.wikipedia.org/wiki/Hash_table[维基百科哈希表条目] 。 + +=== 数据结构 + +通过制定用于访问容器桶的接口,该标准实质上要求哈希表必须采用闭寻址方案。 + +理论上完全可以设计采用其他实现方式的哈希表。例如,采用开放寻址法,利用查找链来模拟桶的行为,但这种做法会带来一些严重问题: + +* 该标准要求指向元素的指针不得失效,因此 +元素无法存储在一个连续的数组中,而需要增加一层间接寻址——这将损失开放定址法的主要优势,即效率和大部分内存收益。 +* 局部迭代器的实现将极为低效,且可能无法 +满足复杂度要求。 +* 此外,标准对迭代器失效的时机也存在限制。由于 +开放寻址法在发生大量冲突时性能会急剧下降,这些限制可能会阻止必要的重新哈希操作。虽然可以通过设置较低的最大负载因子来规避此问题——但标准要求该值初始必须设为 1.0。 +* 由于该标准的制定主要着眼于闭寻址方案, +若性能表现不符预期,将会引发用户的困惑。 + +因此采用闭寻址方式。 + +=== 桶数量 + +选择哈希表桶数量有两种主流方法:一种是采用质数作为桶的数量,另一种则是使用2的幂次方。 + +使用质数数量的桶,并通过哈希函数结果取模来选择桶,通常能获得良好的分布效果。其缺点在于所需的取模运算开销较大。这是该容器在大多数情况下之前的做法。 + +使用2的幂次方可加快桶选择速度,但代价是牺牲哈希值的高位比特。对于一些特殊设计的哈希函数,这样做仍能获得良好效果,但由于容器需要兼容任意哈希函数,因此不能依赖这种方法。 + +为避免此问题,可对哈希函数施加转换操作(具体示例可参阅 http://web.archive.org/web/20121102023700/http://www.concentric.net/~Ttwang/tech/inthash.htm[Thomas Wang整数哈希函数文章])。但此类变换需知晓哈希值比特数,故仅在 `size++_++t` 为64位时才会启用。 + +自1.79.0版起,改用 https://en.wikipedia.org/wiki/Hash_function#Fibonacci_hashing[斐波那契哈希] 。在此实现中,桶编号通过公式 `(h * m) >> (w - k)` 确定:其中 `h` 为哈希值, `m` 为 `2^w` 除以黄金分割比的值, `w` 为字长(32位或64位), `2^k` 表示桶的数量。此方法在速度和分布均匀性之间实现了较好的平衡。 + +自1.80.0版起,结合精密取模运算选用质数桶量,无需1.79.0版所用的用户哈希函数结果"混合"处理。 + +== 开地址容器 + +C++ 标准中对无序关联容器的规范对允许的实现方式施加了严格的限制,其中最重要的一点是隐式假定使用闭地址法。稍微放宽这一规范,则为提供能够充分利用开放寻址技术的容器变体开辟了可能性。 + +`boost::unordered++_++flat++_++set` / `unordered++_++node++_++set` 及 `boost::unordered++_++flat++_++map` / `unordered++_++node++_++map` 的设计遵循Peter Dimov的 https://pdimov.github.io/articles/unordered_dev_plan.html[Boost.Unordered开发计划] 中提出的指导原则。下文将探讨其中最核心的设计理念。 + +=== 哈希函数 + +基于其丰富功能与跨平台互操作性, `boost::hash` 仍是开放寻址容器的默认哈希函数。但由于其对整型等基础类型实现的 boost::hash 缺乏开放寻址所需的统计特性,我们额外增加了后混合处理阶段: + +{nbsp}{nbsp}{nbsp}{nbsp} _a_ <- _h_ *mul* _C_, + {nbsp}{nbsp}{nbsp}{nbsp} _h_ <- *high*(_a_) *xor* *low*(_a_), + +其中 *mul* 是 _扩展乘法_(64 位架构中为 128 位,32 位环境中为 64 位),*high* 和 *low* 分别表示扩展字的高位部分和低位部分。在 64 位架构中,_C_ 是 2^64^∕https://en.wikipedia.org/wiki/Golden_ratio[_φ_] 的整数部分;而在 32 位架构中,_C_ = 0xE817FB2Du 取自 https://arxiv.org/abs/2001.05304[Steele and Vigna (2021)^]。 + +当使用直接适合开放定址的哈希函数时,可以通过专用的 `link:../../../../container_hash/doc/html/hash.html#ref_hash_is_avalanching[hash_is_avalanching]` 特征来退出后混合操作。针对字符串类型的 `boost::hash` 特化已被标记为雪崩式哈希。 + +=== 平台互操作性 + +只要不同编译器中的 `std::size_t` 大小相同,并且用户提供的哈希函数和相等谓词也具有互操作性,那么 `boost::unordered_flat_set`/`unordered_node_set` 和 `boost::unordered_flat_map`/`unordered_node_map` 的可观测行为在不同编译器之间是确定相同的——这包括对于相同的操作序列,元素的排列顺序也完全相同。 + +尽管实现内部在可用时会使用 SIMD 技术,例如 https://en.wikipedia.org/wiki/SSE2[SSE2^] 和 https://en.wikipedia.org/wiki/ARM_architecture_family#Advanced_SIMD_(NEON)[Neon^],但这并不影响互操作性。例如,在带有 SSE2 的 x64 模式 Intel CPU 上使用 Visual Studio 的行为,与在不支持任何 SIMD 技术的 IBM s390x 上使用 GCC 的行为相同。 + +== 并发容器 + +Boost.Unordered 开放寻址容器所使用的相同数据结构也被选为 `boost::concurrent_flat_set`/`boost::concurrent_node_set` 和 `boost::concurrent_flat_map`/`boost::concurrent_node_map` 的基础: + +* 无论是在非并发环境还是并发环境下,开放寻址都比闭地址方案更快。 +并发场景中也是如此。 +* 开放寻址的内存布局极其适合在最小化锁竞争的条件下进行并发访问与修改。 +通过最小化锁定。特别是,元数据数组可用于实现查找操作,该操作在实际元素比较的最后一步之前都是无锁的。 +* 并发容器与 Boost.Unordered 扁平容器具有 +内存布局兼容性,支持其与非并发对应容器之间 xref:concurrent.adoc#concurrent_interoperability_with_non_concurrent_containers[快速双向传输] 所有元素。 + +=== 哈希函数与平台互操作性 + +在 xref:#rationale_hash_function[哈希函数默认值] 和 xref:#rationale_platform_interoperability[平台互操作性] 方面,并发容器与 Boost.Unordered 开放寻址容器做出了相同的决策并提供了相同的保证。 diff --git a/doc/modules/ROOT/pages/ref_zh_Hans.adoc b/doc/modules/ROOT/pages/ref_zh_Hans.adoc new file mode 100644 index 0000000..cbd50bb --- /dev/null +++ b/doc/modules/ROOT/pages/ref_zh_Hans.adoc @@ -0,0 +1,39 @@ +[#reference] += 参考 + +* xref:reference/header_unordered_map_fwd.adoc[++++++++++++ 概要] +* xref:reference/header_unordered_map_top.adoc[++++++++++++ 概要] +* xref:reference/header_unordered_map.adoc[++++++++++++ 概要] +* xref:reference/unordered_map.adoc[类模板 ++++++unordered_map++++++] +* xref:reference/unordered_multimap.adoc[类模板 ++++++unordered_multimap++++++] +* xref:reference/header_unordered_set_fwd.adoc[++++++++++++ 概要] +* xref:reference/header_unordered_set_top.adoc[++++++++++++ 概要] +* xref:reference/header_unordered_set.adoc[++++++++++++ 概要] +* xref:reference/unordered_set.adoc[类模板 ++++++unordered_set++++++] +* xref:reference/unordered_multiset.adoc[类模板 ++++++unordered_multiset++++++] +* xref:reference/hash_traits.adoc[哈希特征] +* xref:reference/stats.adoc[统计信息] +* xref:reference/header_unordered_flat_map_fwd.adoc[++++++++++++ 概要] +* xref:reference/header_unordered_flat_map.adoc[++++++++++++ 概要] +* xref:reference/unordered_flat_map.adoc[类模板 ++++++unordered_flat_map++++++] +* xref:reference/header_unordered_flat_set_fwd.adoc[++++++++++++ 概要] +* xref:reference/header_unordered_flat_set.adoc[++++++++++++ 概要] +* xref:reference/unordered_flat_set.adoc[类模板 ++++++unordered_flat_set++++++] +* xref:reference/header_unordered_node_map_fwd.adoc[++++++++++++ 概要] +* xref:reference/header_unordered_node_map.adoc[++++++++++++ 概要] +* xref:reference/unordered_node_map.adoc[类模板 ++++++unordered_node_map++++++] +* xref:reference/header_unordered_node_set_fwd.adoc[++++++++++++ 概要] +* xref:reference/header_unordered_node_set.adoc[++++++++++++ 概要] +* xref:reference/unordered_node_set.adoc[类模板 ++++++unordered_node_set++++++] +* xref:reference/header_concurrent_flat_map_fwd.adoc[++++++++++++ 概要] +* xref:reference/header_concurrent_flat_map.adoc[++++++++++++ 概要] +* xref:reference/concurrent_flat_map.adoc[类模板 ++++++concurrent_flat_map++++++] +* xref:reference/header_concurrent_flat_set_fwd.adoc[++++++++++++ 概要] +* xref:reference/header_concurrent_flat_set.adoc[++++++++++++ 概要] +* xref:reference/concurrent_flat_set.adoc[类模板 ++++++concurrent_flat_set++++++] +* xref:reference/header_concurrent_node_map_fwd.adoc[++++++++++++ 概要] +* xref:reference/header_concurrent_node_map.adoc[++++++++++++ 概要] +* xref:reference/concurrent_node_map.adoc[类模板 ++++++concurrent_node_map++++++] +* xref:reference/header_concurrent_node_set_fwd.adoc[++++++++++++ 概要] +* xref:reference/header_concurrent_node_set.adoc[++++++++++++ 概要] +* xref:reference/concurrent_node_set.adoc[Class Template ++++++concurrent_node_set++++++] diff --git a/doc/modules/ROOT/pages/reference/concurrent_flat_map_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/concurrent_flat_map_zh_Hans.adoc new file mode 100644 index 0000000..7679068 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/concurrent_flat_map_zh_Hans.adoc @@ -0,0 +1,1406 @@ +[#concurrent_flat_map] +== 类模板 concurrent_flat_map + +:idprefix: concurrent_flat_map_ + +`boost::concurrent_flat_map` — 一种哈希表,将唯一键与对应值关联,并允许在无需外部同步机制的情况下进行并发的元素插入、删除、查找和访问。 + +尽管它充当容器的角色,boost::concurrent_flat_map 并未遵循标准 C++ 的 https://en.cppreference.com/w/cpp/named_req/Container[Container^] 概念。特别地,它不提供迭代器及相关操作(例如 begin、end 等)。元素的访问和修改是通过用户提供的 访问函数 来完成的,这些函数被传递给 concurrent_flat_map 的操作,并以受控的方式在内部执行。这种基于访问的 API 能够实现低竞争并发的使用场景。 + +`boost::concurrent++_++flat++_++map` 的内部数据结构与 `boost::unordered++_++flat++_++map` 类似。由于采用开放寻址技术, `value++_++type` 必须支持移动构造,且在重哈希过程中无法保持指针稳定性。 + +=== 概要 + +[listing,subs="+macros,+quotes"] +----- +// #include xref:reference/header_concurrent_flat_map.adoc[``] + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + class concurrent_flat_map { + public: + // types + using key_type = Key; + using mapped_type = T; + using value_type = std::pair; + using init_type = std::pair< + typename std::remove_const::type, + typename std::remove_const::type + >; + using hasher = Hash; + using key_equal = Pred; + using allocator_type = Allocator; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + using stats = xref:reference/stats.adoc#stats_stats_type[__stats-type__]; // if statistics are xref:concurrent_flat_map_boost_unordered_enable_stats[enabled] + + // constants + static constexpr size_type xref:#concurrent_flat_map_constants[bulk_visit_size] = _implementation-defined_; + + // construct/copy/destroy + xref:#concurrent_flat_map_default_constructor[concurrent_flat_map](); + explicit xref:#concurrent_flat_map_bucket_count_constructor[concurrent_flat_map](size_type n, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + template + xref:#concurrent_flat_map_iterator_range_constructor[concurrent_flat_map](InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#concurrent_flat_map_copy_constructor[concurrent_flat_map](const concurrent_flat_map& other); + xref:#concurrent_flat_map_move_constructor[concurrent_flat_map](concurrent_flat_map&& other); + template + xref:#concurrent_flat_map_iterator_range_constructor_with_allocator[concurrent_flat_map](InputIterator f, InputIterator l,const allocator_type& a); + explicit xref:#concurrent_flat_map_allocator_constructor[concurrent_flat_map](const Allocator& a); + xref:#concurrent_flat_map_copy_constructor_with_allocator[concurrent_flat_map](const concurrent_flat_map& other, const Allocator& a); + xref:#concurrent_flat_map_move_constructor_with_allocator[concurrent_flat_map](concurrent_flat_map&& other, const Allocator& a); + xref:#concurrent_flat_map_move_constructor_from_unordered_flat_map[concurrent_flat_map](unordered_flat_map&& other); + xref:#concurrent_flat_map_initializer_list_constructor[concurrent_flat_map](std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#concurrent_flat_map_bucket_count_constructor_with_allocator[concurrent_flat_map](size_type n, const allocator_type& a); + xref:#concurrent_flat_map_bucket_count_constructor_with_hasher_and_allocator[concurrent_flat_map](size_type n, const hasher& hf, const allocator_type& a); + template + xref:#concurrent_flat_map_iterator_range_constructor_with_bucket_count_and_allocator[concurrent_flat_map](InputIterator f, InputIterator l, size_type n, + const allocator_type& a); + template + xref:#concurrent_flat_map_iterator_range_constructor_with_bucket_count_and_hasher[concurrent_flat_map](InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); + xref:#concurrent_flat_map_initializer_list_constructor_with_allocator[concurrent_flat_map](std::initializer_list il, const allocator_type& a); + xref:#concurrent_flat_map_initializer_list_constructor_with_bucket_count_and_allocator[concurrent_flat_map](std::initializer_list il, size_type n, + const allocator_type& a); + xref:#concurrent_flat_map_initializer_list_constructor_with_bucket_count_and_hasher_and_allocator[concurrent_flat_map](std::initializer_list il, size_type n, const hasher& hf, + const allocator_type& a); + xref:#concurrent_flat_map_destructor[~concurrent_flat_map](); + concurrent_flat_map& xref:#concurrent_flat_map_copy_assignment[operator++=++](const concurrent_flat_map& other); + concurrent_flat_map& xref:#concurrent_flat_map_move_assignment[operator++=++](concurrent_flat_map&& other) ++noexcept( + (boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_move_assignment::value) && + std::is_same::value);++ + concurrent_flat_map& xref:#concurrent_flat_map_initializer_list_assignment[operator++=++](std::initializer_list); + allocator_type xref:#concurrent_flat_map_get_allocator[get_allocator]() const noexcept; + + + // visitation + template size_t xref:#concurrent_flat_map_cvisit[visit](const key_type& k, F f); + template size_t xref:#concurrent_flat_map_cvisit[visit](const key_type& k, F f) const; + template size_t xref:#concurrent_flat_map_cvisit[cvisit](const key_type& k, F f) const; + template size_t xref:#concurrent_flat_map_cvisit[visit](const K& k, F f); + template size_t xref:#concurrent_flat_map_cvisit[visit](const K& k, F f) const; + template size_t xref:#concurrent_flat_map_cvisit[cvisit](const K& k, F f) const; + + template + size_t xref:concurrent_flat_map_bulk_visit[visit](FwdIterator first, FwdIterator last, F f); + template + size_t xref:concurrent_flat_map_bulk_visit[visit](FwdIterator first, FwdIterator last, F f) const; + template + size_t xref:concurrent_flat_map_bulk_visit[cvisit](FwdIterator first, FwdIterator last, F f) const; + + template size_t xref:#concurrent_flat_map_cvisit_all[visit_all](F f); + template size_t xref:#concurrent_flat_map_cvisit_all[visit_all](F f) const; + template size_t xref:#concurrent_flat_map_cvisit_all[cvisit_all](F f) const; + template + void xref:#concurrent_flat_map_parallel_cvisit_all[visit_all](ExecutionPolicy&& policy, F f); + template + void xref:#concurrent_flat_map_parallel_cvisit_all[visit_all](ExecutionPolicy&& policy, F f) const; + template + void xref:#concurrent_flat_map_parallel_cvisit_all[cvisit_all](ExecutionPolicy&& policy, F f) const; + + template bool xref:#concurrent_flat_map_cvisit_while[visit_while](F f); + template bool xref:#concurrent_flat_map_cvisit_while[visit_while](F f) const; + template bool xref:#concurrent_flat_map_cvisit_while[cvisit_while](F f) const; + template + bool xref:#concurrent_flat_map_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f); + template + bool xref:#concurrent_flat_map_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f) const; + template + bool xref:#concurrent_flat_map_parallel_cvisit_while[cvisit_while](ExecutionPolicy&& policy, F f) const; + + // capacity + ++[[nodiscard]]++ bool xref:#concurrent_flat_map_empty[empty]() const noexcept; + size_type xref:#concurrent_flat_map_size[size]() const noexcept; + size_type xref:#concurrent_flat_map_max_size[max_size]() const noexcept; + + // modifiers + template bool xref:#concurrent_flat_map_emplace[emplace](Args&&... args); + bool xref:#concurrent_flat_map_copy_insert[insert](const value_type& obj); + bool xref:#concurrent_flat_map_copy_insert[insert](const init_type& obj); + bool xref:#concurrent_flat_map_move_insert[insert](value_type&& obj); + bool xref:#concurrent_flat_map_move_insert[insert](init_type&& obj); + template size_type xref:#concurrent_flat_map_insert_iterator_range[insert](InputIterator first, InputIterator last); + size_type xref:#concurrent_flat_map_insert_initializer_list[insert](std::initializer_list il); + + template bool xref:#concurrent_flat_map_emplace_or_cvisit[emplace_or_visit](Args&&... args, F&& f); + template bool xref:#concurrent_flat_map_emplace_or_cvisit[emplace_or_cvisit](Args&&... args, F&& f); + template bool xref:#concurrent_flat_map_copy_insert_or_cvisit[insert_or_visit](const value_type& obj, F f); + template bool xref:#concurrent_flat_map_copy_insert_or_cvisit[insert_or_cvisit](const value_type& obj, F f); + template bool xref:#concurrent_flat_map_copy_insert_or_cvisit[insert_or_visit](const init_type& obj, F f); + template bool xref:#concurrent_flat_map_copy_insert_or_cvisit[insert_or_cvisit](const init_type& obj, F f); + template bool xref:#concurrent_flat_map_move_insert_or_cvisit[insert_or_visit](value_type&& obj, F f); + template bool xref:#concurrent_flat_map_move_insert_or_cvisit[insert_or_cvisit](value_type&& obj, F f); + template bool xref:#concurrent_flat_map_move_insert_or_cvisit[insert_or_visit](init_type&& obj, F f); + template bool xref:#concurrent_flat_map_move_insert_or_cvisit[insert_or_cvisit](init_type&& obj, F f); + template + size_type xref:#concurrent_flat_map_insert_iterator_range_or_visit[insert_or_visit](InputIterator first, InputIterator last, F f); + template + size_type xref:#concurrent_flat_map_insert_iterator_range_or_visit[insert_or_cvisit](InputIterator first, InputIterator last, F f); + template size_type xref:#concurrent_flat_map_insert_initializer_list_or_visit[insert_or_visit](std::initializer_list il, F f); + template size_type xref:#concurrent_flat_map_insert_initializer_list_or_visit[insert_or_cvisit](std::initializer_list il, F f); + + template + bool xref:#concurrent_flat_map_emplace_and_cvisit[emplace_and_visit](Args&&... args, F1&& f1, F2&& f2); + template + bool xref:#concurrent_flat_map_emplace_and_cvisit[emplace_and_cvisit](Args&&... args, F1&& f1, F2&& f2); + template bool xref:#concurrent_flat_map_copy_insert_and_cvisit[insert_and_visit](const value_type& obj, F1 f1, F2 f2); + template bool xref:#concurrent_flat_map_copy_insert_and_cvisit[insert_and_cvisit](const value_type& obj, F1 f1, F2 f2); + template bool xref:#concurrent_flat_map_copy_insert_and_cvisit[insert_and_visit](const init_type& obj, F1 f1, F2 f2); + template bool xref:#concurrent_flat_map_copy_insert_and_cvisit[insert_and_cvisit](const init_type& obj, F1 f1, F2 f2); + template bool xref:#concurrent_flat_map_move_insert_and_cvisit[insert_and_visit](value_type&& obj, F1 f1, F2 f2); + template bool xref:#concurrent_flat_map_move_insert_and_cvisit[insert_and_cvisit](value_type&& obj, F1 f1, F2 f2); + template bool xref:#concurrent_flat_map_move_insert_and_cvisit[insert_and_visit](init_type&& obj, F1 f1, F2 f2); + template bool xref:#concurrent_flat_map_move_insert_and_cvisit[insert_and_cvisit](init_type&& obj, F1 f1, F2 f2); + template + size_type xref:#concurrent_flat_map_insert_iterator_range_and_visit[insert_and_visit](InputIterator first, InputIterator last, F1 f1, F2 f2); + template + size_type xref:#concurrent_flat_map_insert_iterator_range_and_visit[insert_and_cvisit](InputIterator first, InputIterator last, F1 f1, F2 f2); + template + size_type xref:#concurrent_flat_map_insert_initializer_list_and_visit[insert_and_visit](std::initializer_list il, F1 f1, F2 f2); + template + size_type xref:#concurrent_flat_map_insert_initializer_list_and_visit[insert_and_cvisit](std::initializer_list il, F1 f1, F2 f2); + + template bool xref:#concurrent_flat_map_try_emplace[try_emplace](const key_type& k, Args&&... args); + template bool xref:#concurrent_flat_map_try_emplace[try_emplace](key_type&& k, Args&&... args); + template bool xref:#concurrent_flat_map_try_emplace[try_emplace](K&& k, Args&&... args); + + template + bool xref:#concurrent_flat_map_try_emplace_or_cvisit[try_emplace_or_visit](const key_type& k, Args&&... args, F&& f); + template + bool xref:#concurrent_flat_map_try_emplace_or_cvisit[try_emplace_or_cvisit](const key_type& k, Args&&... args, F&& f); + template + bool xref:#concurrent_flat_map_try_emplace_or_cvisit[try_emplace_or_visit](key_type&& k, Args&&... args, F&& f); + template + bool xref:#concurrent_flat_map_try_emplace_or_cvisit[try_emplace_or_cvisit](key_type&& k, Args&&... args, F&& f); + template + bool xref:#concurrent_flat_map_try_emplace_or_cvisit[try_emplace_or_visit](K&& k, Args&&... args, F&& f); + template + bool xref:#concurrent_flat_map_try_emplace_or_cvisit[try_emplace_or_cvisit](K&& k, Args&&... args, F&& f); + + template + bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_visit](const key_type& k, Args&&... args, F1&& f1, F2&& f2); + template + bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_cvisit](const key_type& k, Args&&... args, F1&& f1, F2&& f2); + template + bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_visit](key_type&& k, Args&&... args, F1&& f1, F2&& f2); + template + bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_cvisit](key_type&& k, Args&&... args, F1&& f1, F2&& f2); + template + bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_visit](K&& k, Args&&... args, F1&& f1, F2&& f2); + template + bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_cvisit](K&& k, Args&&... args, F1&& f1, F2&& f2); + + template bool xref:#concurrent_flat_map_insert_or_assign[insert_or_assign](const key_type& k, M&& obj); + template bool xref:#concurrent_flat_map_insert_or_assign[insert_or_assign](key_type&& k, M&& obj); + template bool xref:#concurrent_flat_map_insert_or_assign[insert_or_assign](K&& k, M&& obj); + + size_type xref:#concurrent_flat_map_erase[erase](const key_type& k); + template size_type xref:#concurrent_flat_map_erase[erase](const K& k); + + template size_type xref:#concurrent_flat_map_erase_if_by_key[erase_if](const key_type& k, F f); + template size_type xref:#concurrent_flat_map_erase_if_by_key[erase_if](const K& k, F f); + template size_type xref:#concurrent_flat_map_erase_if[erase_if](F f); + template void xref:#concurrent_flat_map_parallel_erase_if[erase_if](ExecutionPolicy&& policy, F f); + + void xref:#concurrent_flat_map_swap[swap](concurrent_flat_map& other) + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_swap::value); + void xref:#concurrent_flat_map_clear[clear]() noexcept; + + template + size_type xref:#concurrent_flat_map_merge[merge](concurrent_flat_map& source); + template + size_type xref:#concurrent_flat_map_merge[merge](concurrent_flat_map&& source); + + // observers + hasher xref:#concurrent_flat_map_hash_function[hash_function]() const; + key_equal xref:#concurrent_flat_map_key_eq[key_eq]() const; + + // map operations + size_type xref:#concurrent_flat_map_count[count](const key_type& k) const; + template + size_type xref:#concurrent_flat_map_count[count](const K& k) const; + bool xref:#concurrent_flat_map_contains[contains](const key_type& k) const; + template + bool xref:#concurrent_flat_map_contains[contains](const K& k) const; + + // bucket interface + size_type xref:#concurrent_flat_map_bucket_count[bucket_count]() const noexcept; + + // hash policy + float xref:#concurrent_flat_map_load_factor[load_factor]() const noexcept; + float xref:#concurrent_flat_map_max_load_factor[max_load_factor]() const noexcept; + void xref:#concurrent_flat_map_set_max_load_factor[max_load_factor](float z); + size_type xref:#concurrent_flat_map_max_load[max_load]() const noexcept; + void xref:#concurrent_flat_map_rehash[rehash](size_type n); + void xref:#concurrent_flat_map_reserve[reserve](size_type n); + + // statistics (if xref:concurrent_flat_map_boost_unordered_enable_stats[enabled]) + stats xref:#concurrent_flat_map_get_stats[get_stats]() const; + void xref:#concurrent_flat_map_reset_stats[reset_stats]() noexcept; + }; + + // Deduction Guides + template>, + class Pred = std::equal_to>, + class Allocator = std::allocator>> + concurrent_flat_map(InputIterator, InputIterator, typename xref:#concurrent_flat_map_deduction_guides[__see below__]::size_type = xref:#concurrent_flat_map_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> concurrent_flat_map, xref:#concurrent_flat_map_iter_mapped_type[__iter-mapped-type__], Hash, + Pred, Allocator>; + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + concurrent_flat_map(std::initializer_list>, + typename xref:#concurrent_flat_map_deduction_guides[__see below__]::size_type = xref:#concurrent_flat_map_deduction_guides[__see below__], Hash = Hash(), + Pred = Pred(), Allocator = Allocator()) + -> concurrent_flat_map; + + template + concurrent_flat_map(InputIterator, InputIterator, typename xref:#concurrent_flat_map_deduction_guides[__see below__]::size_type, Allocator) + -> concurrent_flat_map, xref:#concurrent_flat_map_iter_mapped_type[__iter-mapped-type__], + boost::hash>, + std::equal_to>, Allocator>; + + template + concurrent_flat_map(InputIterator, InputIterator, Allocator) + -> concurrent_flat_map, xref:#concurrent_flat_map_iter_mapped_type[__iter-mapped-type__], + boost::hash>, + std::equal_to>, Allocator>; + + template + concurrent_flat_map(InputIterator, InputIterator, typename xref:#concurrent_flat_map_deduction_guides[__see below__]::size_type, Hash, + Allocator) + -> concurrent_flat_map, xref:#concurrent_flat_map_iter_mapped_type[__iter-mapped-type__], Hash, + std::equal_to>, Allocator>; + + template + concurrent_flat_map(std::initializer_list>, typename xref:#concurrent_flat_map_deduction_guides[__see below__]::size_type, + Allocator) + -> concurrent_flat_map, std::equal_to, Allocator>; + + template + concurrent_flat_map(std::initializer_list>, Allocator) + -> concurrent_flat_map, std::equal_to, Allocator>; + + template + concurrent_flat_map(std::initializer_list>, typename xref:#concurrent_flat_map_deduction_guides[__see below__]::size_type, + Hash, Allocator) + -> concurrent_flat_map, Allocator>; + +} // namespace unordered +} // namespace boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_Key_ +.2+|`Key` and `T` must be https://en.cppreference.com/w/cpp/named_req/MoveConstructible[MoveConstructible^]. +`std::pair` 必须能够从任何可转换为其的 `std::pair` 对象出发,在表中进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^] 构造,并且也必须可以从表中进行 https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] 擦除。 + +|_T_ + +|_Hash_ +|A unary function object type that acts a hash function for a `Key`. It takes a single argument of type `Key` and returns a value of type `std::size_t`. + +|_Pred_ +|A binary function object that induces an equivalence relation on values of type `Key`. It takes two arguments of type `Key` and returns a value of type `bool`. + +|_Allocator_ +|An allocator whose value type is the same as the table's value type. +支持使用https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[花式指针] 的分配器。 + +|=== + +容器的元素存储在内部的__桶数组__中。元素根据其哈希码被插入到对应的桶中,但如果该桶已被占用(即发生__冲突__),则会使用原始位置附近可用的桶。 + +桶数组的大小可通过调用 `insert` / `emplace` 自动增加,也可通过调用 `rehash` / `reserve` 进行调整。容器的__负载因子__(元素数量与桶数量的比值)永远不会超过 `max++_++load++_++factor()` ,但在小规模数据情况下,实现可能允许更高的负载因子。 + +若 link:../../../../../container_hash/doc/html/hash.html#ref_hash_is_avalanchinghash[`hash++_++is++_++avalanching`]`++<++Hash++>++::value` 为 `true` ,则直接使用哈希函数;否则,会添加一个位混合后处理阶段以提高哈希质量,但会牺牲额外的计算成本。 + +--- + +=== 并发要求与保证 + +要求对同一 `Hash` 或 `Pred` 常量实例并发调用 `operator()` 时不得引入数据竞争。对于 `Alloc` (即 `Allocator` 或其重绑定后的任意分配器类型),在同一实例 `al` 上并发调用以下操作时不得引入数据竞争: + +* 从Alloc重新绑定后的分配器的al进行拷贝构造 +* `std::allocator_traits::allocate` +* `std::allocator_traits::deallocate` +* `std::allocator_traits::construct` +* `std::allocator_traits::destroy` + +通常而言,若 `Hash` 、 `Pred` 和 `Allocator` 这些类型不包含状态,或其操作仅涉及对内部数据成员的常量访问,即可满足上述要求。 + +除了析构操作外,在同一个 `concurrent_flat_map` 实例上并发调用任何操作都不会引入数据竞争——即这些操作是线程安全的。 + +若某个操作 *op* 被显式指定为__阻塞于__ `x` (其中 `x` 为 `boost::concurrent_flat_map` 实例),则先前对 `x` 的阻塞操作将与 *op* 同步。因此,在多线程场景中,对同一 `concurrent_flat_map` 的阻塞操作将按顺序执行。 + +若某个操作仅在触发内部重哈希时才会阻塞于 _`x`_,则称该操作阻塞于 _`x`_ 的重哈希过程。 + +当由 `boost::concurrent_flat_map` 内部执行时,用户提供的访问函数对传入元素执行以下操作不会引入数据竞争: + +* 对元素的读取访问。 +* 对元素的非可变修改。 +* 对元素的可变修改: +** 在容器接受两个访问函数的操作中,此条件始终适用于第一个访问函数。 ** 在名称不包含 `cvisit` 的非常量容器函数中,此条件适用于最后一个(或唯一一个)访问函数。 + +任何插入或修改元素 `e` 的 `boost::concurrent_flat_map operation` 操作,都会与针对 `e` 的内部访问函数调用同步。 + +由 `boost::concurrent_flat_map` 容器 `x` 执行的访问函数不得调用 `x` 上的任何操作;仅当对另一 `boost::concurrent_flat_map` 实例 `y` 的并发的未完成操作不直接或间接访问 `x` 时,才允许调用实例 `y` 上的操作。 + +--- + +=== 配置宏 + +==== `BOOST_UNORDERED_DISABLE_REENTRANCY_CHECK` + +在调试版本中(更准确地说,当未定义 link:../../../../../assert/doc/html/assert.html#boost_assert_is_void[`BOOST_ASSERT_IS_VOID`] 时),系统会检测__容器重入__行为(即在访问 `m` 元素的函数内部非法调用 `m` 上的操作),并通过 `BOOST_ASSERT_MSG` 发出信号。若需关注运行时速度,可通过全局定义此宏来禁用该功能。 + +--- + +==== `BOOST_UNORDERED_ENABLE_STATS` + +全局定义此宏以启用容器的 xref:reference/stats.adoc#stats[统计计算] 功能。请注意,此选项会降低多数操作的总体性能。 + +--- + +=== 常量 + +```cpp static constexpr size_type bulk_visit_size; ``` + +xref:concurrent_flat_map_bulk_visit [批量访问] 操作内部使用的块大小。 + +=== 构造函数 + +==== 默认构造函数 +```c++ concurrent_flat_map(); ``` + +使用hasher()作为哈希函数、key_equal()作为键相等谓词、allocator_type()作为分配器,构造一个空表。 + +[horizontal] +后置条件:size() == 0 +要求:若使用默认构造方式,则hasher、key_equal和allocator_type必须满足https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[默认可构造]要求。 + +--- + +==== 桶数构造函数 +```c++ explicit concurrent_flat_map(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); ``` + +构造一个至少包含n个桶的空表,使用hf作为哈希函数、eql作为键相等谓词、a作为分配器。 + +[horizontal] +后置条件:size() == 0 +要求:若使用默认构造方式,则hasher、key_equal和allocator_type必须满足https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[默认可构造]要求。 + +--- + +==== 迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + concurrent_flat_map(InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `eql` 作为键相等性谓词、 `a` 作为分配器,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求;; 若使用默认值,则 `hasher` 、 `key++_++equal` 和 `allocator++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 复制构造函数 +```c++ concurrent_flat_map(concurrent_flat_map const& other); ``` + +拷贝构造函数。复制容器内的元素、哈希函数、谓词以及分配器。 + +若Allocator::select_on_container_copy_construction存在且签名正确,则分配器将根据其返回值构造。 + +[horizontal] +要求:value_type 支持复制构造。 并发特性:阻塞于 other + +--- + +==== 移动构造函数 +```c++ concurrent_flat_map(concurrent_flat_map&& other); ``` + +移动构造函数。other 的内部桶数组会直接转移到新表中。哈希函数、谓词和分配器均从 other 移动构造而来。如果启用了 xref:concurrent_flat_map_boost_unordered_enable_stats [统计功能],则从 other 转移内部统计信息,并调用 other.reset_stats()。 + +[horizontal] +并发特性:阻塞于 other + +--- + +==== 带分配器的迭代器范围构造函数 +```c++ template concurrent_flat_map(InputIterator f, InputIterator l, const allocator_type& a); ``` + +以a作为分配器,使用默认哈希函数与键相等谓词构造空表,并将[f, l)范围内的元素插入其中。 + +[horizontal] +要求:hasher、key_equal 必须满足https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[默认可构造]要求。 + +--- + +==== 分配器构造函数 +```c++ explicit concurrent_flat_map(Allocator const& a); ``` + +使用分配器a构造一个空表。 + +--- + +==== 带分配器的复制构造函数 +```c++ concurrent_flat_map(concurrent_flat_map const& other, Allocator const& a); ``` + +构造一个表,复制other的容器元素、哈希函数和谓词,但使用分配器a。 + +[horizontal] +并发特性:阻塞于 other + +--- + +==== 带分配器的移动构造函数 +```c++ concurrent_flat_map(concurrent_flat_map&& other, Allocator const& a); ``` + +若a == other.get_allocator(),则other的元素会直接转移到新表中;否则,元素从other移动构造而来。 +哈希函数与谓词从other移动构造,分配器从a拷贝构造。 +如果启用了 xref:concurrent_flat_map_boost_unordered_enable_stats [统计功能]: +当且仅当a == other.get_allocator()时,从other转移内部统计信息 +始终调用other.reset_stats() + +[horizontal] +并发特性:阻塞于 other + +--- + +==== 从 unordered_flat_map 的移动构造函数 + +```c++ concurrent_flat_map(unordered_flat_map&& other); ``` + +从 xref:#unordered_flat_map [unordered_flat_map] 进行移动构造。other的内部桶数组会直接转移到新容器中。哈希函数、谓词和分配器均从other移动构造而来。如果启用了 xref:concurrent_flat_map_boost_unordered_enable_stats [统计功能],则从other转移内部统计信息,并调用other.reset_stats()。 + +[horizontal] +复杂度:O (`bucket_count()`) + +--- + +==== 初始化列表构造函数 +[source,c++,subs="+quotes"] +---- +concurrent_flat_map(std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `eql` 作为键相等性谓词、 `a` 作为分配器,并 `il` 中的元素插入其中。 + +[horizontal] +要求;; 若使用默认值,则 `hasher` 、 `key++_++equal` 和 `allocator++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带分配器的桶数构造函数 +```c++ concurrent_flat_map(size_type n, allocator_type const& a); ``` + +构造一个至少包含 n 个桶的空表,使用 hf 作为哈希函数、默认的键相等谓词,并以 a 作为分配器。 + +[horizontal] +后置条件:size() == 0 +要求:hasher 和 key_equal 必须满足https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[默认可构造]要求。 + +--- + +==== 带哈希函数和分配器的桶数构造函数 +```c++ concurrent_flat_map(size_type n, hasher const& hf, allocator_type const& a); ``` + +构造一个至少包含 n 个桶的空表,使用 hf 作为哈希函数、默认键相等谓词,并以 a 作为分配器。 + +[horizontal] +后置条件:size() == 0 +要求:key_equal 必须满足https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[默认可构造]。 + +--- + +==== 带桶数和分配器的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + concurrent_flat_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 作为分配器以及默认的哈希函数和键相等性谓词,并将 `++[++f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:hasher、key_equal 必须满足https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[默认可构造]要求。 + +--- + +==== 带桶数和哈希函数的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- + template + concurrent_flat_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `a` 作为分配器以及默认的键相等性谓词,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求;; `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带分配器的初始化列表构造函数 + +```c++ concurrent_flat_map(std::initializer_list il, const allocator_type& a); ``` + +使用分配器a以及默认哈希函数、键相等谓词构造空表,并将il中的元素插入表中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 带桶数和分配器的初始化列表构造函数 + +```c++ concurrent_flat_map(std::initializer_list il, size_type n, const allocator_type& a); ``` + +构造一个至少包含 n 个桶的空表,使用分配器 a 以及默认哈希函数、键相等谓词,并将 il 中的元素插入表中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 带桶数、哈希函数和分配器的初始化列表构造函数 + +```c++ concurrent_flat_map(std::initializer_list il, size_type n, const hasher& hf, const allocator_type& a); ``` + +构造一个至少包含 n 个桶的空表,使用 hf 作为哈希函数、a 作为分配器、默认键相等谓词,并将 il 中的元素插入表中。 + +[horizontal] +要求;; `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +=== 析构函数 + +```c++ + +[horizontal] +备注:析构函数会作用于所有元素,且所有内存都会被释放。 + +--- + +=== 赋值操作 + +==== 复制赋值 + +```c++ concurrent_flat_map& operator=(concurrent_flat_map const& other); ``` + +赋值运算符。销毁原有的所有元素,从other复制赋值哈希函数与谓词;若Alloc::propagate_on_container_copy_assignment存在且其值为true,则从other复制赋值分配器;最终插入other元素的副本。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入^] 要求。并发:阻塞 `*this` 和 `other`。 + +--- + +==== 移动赋值 +```c++ concurrent_flat_map& operator=(concurrent_flat_map&& other) noexcept((boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_move_assignment::value) && std::is_same::value); ``` +移动赋值运算符。销毁当前容器原有的所有元素,交换other的哈希函数与谓词;若Alloc::propagate_on_container_move_assignment存在且其值为true,则从other移动赋值分配器。 +若此时分配器与other.get_allocator()相等,则直接转移other的内部桶数组至当前容器;否则,插入other元素的移动构造副本。 +如果启用了统计功能: +当且仅当最终分配器与other.get_allocator()相等时,从other转移内部统计信息 +始终调用other.reset_stats() + +[horizontal] +并发:阻塞于 *this 和 other。 + +--- + +==== 初始化列表赋值 +```c++ concurrent_flat_map& operator=(std::initializer_list il); ``` + +并发:阻塞于 *this 和 other。 +从初始化列表赋值。销毁所有先前存在的元素。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入^] 要求。并发:阻塞 `*this`。 + +--- + +=== 访问操作 + +==== [c]visit + +```c++ template size_t visit(const key_type& k, F f); template size_t visit(const key_type& k, F f) const; template size_t cvisit(const key_type& k, F f) const; template size_t visit(const K& k, F f); template size_t visit(const K& k, F f) const; template size_t cvisit(const K& k, F f) const; ``` + +若存在键与 k 等价的元素 x,则以 x 的引用调用函数 f。 +当且仅当当前容器 *this 为常量(const)时,该引用为常量引用。 + +[horizontal] +返回值:访问过的元素数量(0 或 1)。 +注意:template 形式的重载仅在 Hash::is_transparent 与 Pred::is_transparent 为合法成员别名时才参与重载决议。 +库假定 Hash 可同时用于 K 类型与 Key 类型,且 Pred 是透明的。 +这支持异构查找,避免了实例化 Key 类型对象带来的开销。 + +--- + +==== 批量访问 + +```c++ template size_t visit(FwdIterator first, FwdIterator last, F f); template size_t visit(FwdIterator first, FwdIterator last, F f) const; template size_t cvisit(FwdIterator first, FwdIterator last, F f) const; ``` + +对范围 [first, last) 中的每个键 k: +如果容器中存在键与 k 等价的元素 x,则以 x 的引用调用函数 f。 +当且仅当当前容器 *this 为常量(const)时,该引用为常量引用。 + +尽管功能上等价于对每个键单独调用 [c]visit,但批量访问因内部流线型优化,通常性能更高。 +建议 std::distance(first,last) 至少达到 bulk_visit_size 阈值时再使用,以获得性能提升;超过该大小后,性能不会进一步提升。 + +[horizontal] +要求:`FwdIterator` 需满足 https://en.cppreference.com/w/cpp/named_req/ForwardIterator[遗留向前迭代器^] 要求({cpp}11 至 {cpp}17),或满足 https://en.cppreference.com/w/cpp/iterator/forward_iterator[`std::forward_iterator`^] 要求({cpp}20 及更高版本)。对于 `K = std::iterator_traits::value_type`,要么 `K` 是 `key_type`,要么 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef。在后一种情况下,库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。该机制支持异构查找,从而避免实例化 `Key` 类型的开销。返回:被访问的元素数量。 + +--- + +==== [c]visit_all + +```c++ template size_t visit_all(F f); template size_t visit_all(F f) const; template size_t cvisit_all(F f) const; ``` + +依次以表中每个元素的引用调用函数 f。 +当且仅当当前容器 *this 为常量(const)时,该引用为常量引用。 + +[horizontal] +返回值:访问到的元素数量。 + +--- + +==== 并行 [c]visit_all + +```c++ template void visit_all(ExecutionPolicy&& policy, F f); template void visit_all(ExecutionPolicy&& policy, F f) const; template void cvisit_all(ExecutionPolicy&& policy, F f) const; ``` + +以表中每个元素的引用调用函数 f。 +当且仅当当前容器 *this 为常量(const)时,该引用为常量引用。 +执行过程将根据指定的执行策略语义进行并行化。 + +[horizontal] +抛出异常:根据所使用执行策略的异常处理机制,如果 f 内部抛出异常,则可能会调用 std::terminate。 +注意:仅在支持 C++17 并行算法的编译器中可用。 +仅当 std::is_execution_policy_v> 为 true 时,这些重载版本才参与重载决议。 +不允许使用无序执行策略。 + +--- + +==== [c]visit_while + +```c++ template bool visit_while(F f); template bool visit_while(F f) const; template bool cvisit_while(F f) const; ``` + +依次以表中每个元素的引用调用函数 f,直到 f 返回 false 或遍历完所有元素。 +当且仅当当前容器 *this 为常量(const)时,该元素引用为常量引用。 + +[horizontal] +返回值:当且仅当 f 曾返回 false 时,整体返回 false。 + +--- + +==== 并行 [c]visit_while + +```c++ template bool visit_while(ExecutionPolicy&& policy, F f); template bool visit_while(ExecutionPolicy&& policy, F f) const; template bool cvisit_while(ExecutionPolicy&& policy, F f) const; ``` + +以表中每个元素的引用调用函数 f,直到 f 返回 false 或遍历完所有元素。 +当且仅当当前容器 *this 为常量(const)时,该元素引用为常量引用。 +执行过程将根据指定的执行策略语义进行并行化。 + +[horizontal] +返回值:当且仅当 f 曾返回 false 时,整体返回 false。 +抛出异常:根据所使用执行策略的异常处理机制,如果 f 内部抛出异常,则可能会调用 std::terminate。 +注意: +仅在支持 C++17 并行算法的编译器中可用。 +仅当 std::is_execution_policy_v> 为 true 时,这些重载版本才参与重载决议。 +不允许使用无序执行策略。 +并行化意味着:即使 f 已返回 false,执行流程也不一定会立即终止;因此,f 可能还会被继续调用以处理后续元素,且这些调用同样可能返回 false。 + +--- + +=== 大小与容量 + +==== 空 + +```c++ [[nodiscard]] bool empty() const noexcept; ``` + +[horizontal] +返回值:size() == 0 + +--- + +==== 大小 + +```c++ size_type size() const noexcept; ``` + +[horizontal] +返回值:表中的元素数量。 + +[horizontal] +注意:在存在并发插入操作时,返回的值可能无法准确反映函数执行后容器的真实大小。 + +--- + +==== max_size + +```c++ size_type max_size() const noexcept; ``` + +[horizontal] +返回值:容器所能容纳的最大元素数量(最大容量)。 + +--- + +=== 修改器 + +==== 原地构造 +```c++ template bool emplace(Args&&... args); ``` + +当且仅当容器中不存在等价键的元素时,才会使用参数 args 构造对象并插入到容器中。 + +[horizontal] +要求:`value_type` 可从 `args` 构造。返回:若执行了插入则返回 `true`。并发:在 `*this` 的 rehash 操作上阻塞。注意:如果执行了 rehash,则指向元素的指针和引用会失效。+ +若 `args...` 的形式为 `k,v`,则仅在确定应插入元素时才构造整个对象,检查时仅使用 `k` 参数。 + +--- + +==== 复制插入 +```c++ bool insert(const value_type& obj); bool insert(const init_type& obj); ``` + +当且仅当容器中不存在等价键的元素时,才将对象 obj 插入到容器中。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入^] 要求。返回:若执行了插入则返回 `true`。并发:在 `*this` 的 rehash 操作上阻塞。注意:如果执行了 rehash,则指向元素的指针和引用会失效。+ +形式为 `insert(x)` 的调用(其中 `x` 可同等转换为 `const value_type&` 和 `const init_type&`)不会产生歧义,并且会选择 `init_type` 重载。 + +--- + +==== 移动插入 +```c++ bool insert(value_type&& obj); bool insert(init_type&& obj); ``` + +当且仅当容器中不存在等价键的元素时,才将对象 obj 插入到容器中。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^] 要求。返回:若执行了插入则返回 `true`。并发:在 `*this` 的 rehash 操作上阻塞。注意:如果执行了 rehash,则指向元素的指针和引用会失效。+ +形式为 `insert(x)` 的调用(其中 `x` 可同等转换为 `value_type&&` 和 `init_type&&`)不会产生歧义,并且会选择 `init_type` 重载。 + +--- + +==== 迭代器范围插入 +```c++ template size_type insert(InputIterator first, InputIterator last); ``` + +等效于 [listing,subs="+macros,+quotes"] +----- + while(first != last) this->xref:#concurrent_flat_map_emplace[emplace](*first++); +----- + +[horizontal] +返回值:成功插入的元素数量。 + +--- + +==== 初始化列表插入 +```c++ size_type insert(std::initializer_list il); ``` + +等效于 [listing,subs="+macros,+quotes"] +----- + this->xref:#concurrent_flat_map_insert_iterator_range[insert](il.begin(), il.end()); +----- + +[horizontal] +返回值:成功插入的元素数量。 + +--- + +==== emplace_or_[c]visit +```c++ template bool emplace_or_visit(Args&&... args, F&& f); template bool emplace_or_cvisit(Args&&... args, F&& f); ``` + +若容器中无等价键的元素,则使用参数 args 构造对象并插入容器; +否则,将等价元素的引用传递给函数 f 并调用 —— 若使用的是 emplace_or_cvisit,则该引用为常量引用。 + +[horizontal] +要求:`value_type` 可从 `args` 构造。返回:若执行了插入则返回 `true`。并发:在 `*this` 的 rehash 操作上阻塞。注意:如果执行了 rehash,则指向元素的指针和引用会失效。+ +该接口仅为展示说明,因为 C++ 不允许在变参参数包之后声明参数 `f`。 + +--- + +==== Copy insert_or_[c]visit +```c++ template bool insert_or_visit(const value_type& obj, F f); template bool insert_or_cvisit(const value_type& obj, F f); template bool insert_or_visit(const init_type& obj, F f); template bool insert_or_cvisit(const init_type& obj, F f); ``` + +当且仅当容器中不存在等价键的元素时,将对象 obj 插入容器; +否则,将等价元素的引用传入函数 f 并调用 ——若使用的是 *_cvisit 重载版本,该引用为常量引用。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入^] 要求。返回:若执行了插入则返回 `true`。并发:在 `*this` 的 rehash 操作上阻塞。注意:如果执行了 rehash,则指向元素的指针和引用会失效。+ +在形式为 `insert_or_[c]visit(obj, f)` 的调用中,接受 `const value_type&` 参数的重载仅当 `std::remove_cv::type>::type` 为 `value_type` 时才参与重载决议。 + +--- + +==== Move insert_or_[c]visit +```c++ template bool insert_or_visit(value_type&& obj, F f); template bool insert_or_cvisit(value_type&& obj, F f); template bool insert_or_visit(init_type&& obj, F f); template bool insert_or_cvisit(init_type&& obj, F f); ``` + +当且仅当容器中不存在等价键的元素时,将对象 obj 插入容器; +否则,将等价元素的引用传入函数 f 并调用 ——若使用的是 *_cvisit 重载版本,该引用为常量引用。 + +[horizontal] +要求:value_type 满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入] 要求。 +返回值:插入成功则返回 true。 +并发:在当前对象 *this 执行重哈希期间阻塞。 +说明:触发重哈希时,会使指向元素的指针和引用失效。 +调用形式为 insert_or_[c]visit(obj, f) 时,仅当 std::remove_reference::type 类型为 value_type,接收 value_type&& 参数的重载函数才会参与重载决议。 + +--- + +==== 迭代器范围插入或访问 +```c++ template size_type insert_or_visit(InputIterator first, InputIterator last, F f); template size_type insert_or_cvisit(InputIterator first, InputIterator last, F f); ``` + +等效于 [listing,subs="+macros,+quotes"] +----- + while(first != last) this->xref:#concurrent_flat_map_emplace_or_cvisit[emplace_or_[c\]visit](*first++, f); +----- + +[horizontal] +返回值:成功插入的元素数量。 + +--- + +==== 初始化列表插入或访问 +```c++ template size_type insert_or_visit(std::initializer_list il, F f); template size_type insert_or_cvisit(std::initializer_list il, F f); ``` + +等效于 [listing,subs="+macros,+quotes"] +----- + this->xref:#concurrent_flat_map_insert_iterator_range_or_visit[insert_or_[c\]visit](il.begin(), il.end(), std::ref(f)); +----- + +[horizontal] +返回值:成功插入的元素数量。 + +--- + +==== emplace_and_[c]visit +```c++ template bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2); template bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2); ``` + +若容器中无等价键的元素,则使用参数args构造对象并插入容器,随后以新创建元素的非常量引用调用f1; +否则,以等价元素的引用调用f2;若使用emplace_and_cvisit,则该引用为常量引用。 + +[horizontal] +要求:`value_type` 可从 `args` 构造。返回:若执行了插入则返回 `true`。并发:在 `*this` 的 rehash 操作上阻塞。注意:如果执行了 rehash,则指向元素的指针和引用会失效。+ +该接口仅为展示说明,因为 C++ 不允许在变参参数包之后声明参数 `f1` 和 `f2`。 + +--- + +==== 复制 insert_and_[c]visit +```c++ template bool insert_and_visit(const value_type& obj, F1 f1, F2 f2); template bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2); template bool insert_and_visit(const init_type& obj, F1 f1, F2 f2); template bool insert_and_cvisit(const init_type& obj, F1 f1, F2 f2); ``` + +当且仅当容器中不存在等价键的元素时,插入obj,随后以新创建元素的非常量引用调用f1; +否则,以等价元素的引用调用f2;若使用*_cvisit重载版本,则该引用为常量引用。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入^] 要求。返回:若执行了插入则返回 `true`。并发:在 `*this` 的 rehash 操作上阻塞。注意:如果执行了 rehash,则指向元素的指针和引用会失效。+ +在形式为 `insert_and_[c]visit(obj, f1, f2)` 的调用中,接受 `const value_type&` 参数的重载仅当 `std::remove_cv::type>::type` 为 `value_type` 时才参与重载决议。 + +--- + +==== 移动 insert_and_[c]visit +```c++ template bool insert_and_visit(value_type&& obj, F1 f1, F2 f2); template bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2); template bool insert_and_visit(init_type&& obj, F1 f1, F2 f2); template bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2); ``` + +当且仅当容器中不存在等价键的元素时,插入obj,随后以新创建元素的非常量引用调用f1; +否则,以等价元素的引用调用f2;若使用*_cvisit重载版本,则该引用为常量引用。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^] 要求。返回:若执行了插入则返回 `true`。并发:在 `*this` 的 rehash 操作上阻塞。注意:如果执行了 rehash,则指向元素的指针和引用会失效。+ +在形式为 `insert_and_[c]visit(obj, f1, f2)` 的调用中,接受 `value_type&&` 参数的重载仅当 `std::remove_reference::type` 为 `value_type` 时才参与重载决议。 + +--- + +==== 迭代器范围插入并访问 +```c++ template size_type insert_or_visit(InputIterator first, InputIterator last, F1 f1, F2 f2); template size_type insert_or_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2); ``` + +等效于 [listing,subs="+macros,+quotes"] +----- + while(first != last) this->xref:#concurrent_flat_map_emplace_and_cvisit[emplace_and_[c\]visit](*first++, f1, f2); +----- + +[horizontal] +返回值:成功插入的元素数量。 + +--- + +==== 初始化列表插入并访问 +```c++ template size_type insert_and_visit(std::initializer_list il, F1 f1, F2 f2); template size_type insert_and_cvisit(std::initializer_list il, F1 f1, F2 f2); ``` + +等效于 [listing,subs="+macros,+quotes"] +----- + this->xref:#concurrent_flat_map_insert_iterator_range_and_visit[insert_and_[c\]visit](il.begin(), il.end(), std::ref(f1), std::ref(f2)); +----- + +[horizontal] +返回值:成功插入的元素数量。 + +--- + +==== try_emplace +```c++ template bool try_emplace(const key_type& k, Args&&... args); template bool try_emplace(key_type&& k, Args&&... args); template bool try_emplace(K&& k, Args&&... args); ``` + +若容器中不存在键为k的元素,则将由k和args构造的元素插入容器。 + +[horizontal] +返回值:插入成功则返回true。 +并发:对当前对象*this进行重哈希时阻塞。 +说明:该函数与 xref:#concurrent_flat_map_emplace [emplace] 类似,区别在于若存在等价键的元素,则不会构造value_type;否则,构造形式为: +// first two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) + +// third overload +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) ``` + +这与 xref:#concurrent_flat_map_emplace [emplace] 不同,后者仅将所有参数转发给value_type的构造函数。 + +Invalidates pointers and references to elements if a rehashing is issued. + +若`Hash::is_transparent`与`Pred::is_transparent`为合法的成员类型别名,则`template`重载版本才会参与重载决议。库假定`Hash`可同时接收`K`与`Key`类型参数调用,且`Pred`为透明比较器。该设计支持异构查找,避免了实例化`Key`类型对象的开销。 + +-- + +--- + +==== try_emplace_or_[c]visit +```c++ template bool try_emplace_or_visit(const key_type& k, Args&&... args, F&& f); template bool try_emplace_or_cvisit(const key_type& k, Args&&... args, F&& f); template bool try_emplace_or_visit(key_type&& k, Args&&... args, F&& f); template bool try_emplace_or_cvisit(key_type&& k, Args&&... args, F&& f); template bool try_emplace_or_visit(K&& k, Args&&... args, F&& f); template bool try_emplace_or_cvisit(K&& k, Args&&... args, F&& f); ``` + +若容器中不存在键为`k`的元素,则将由`k`和`args`构造的元素插入容器。否则,以等价元素的引用调用`f`;若使用`*_cvisit`重载版本,则该引用为常量引用。 + +[horizontal] +返回值:插入成功则返回true。 +并发:对当前对象*this进行重哈希时阻塞。 +说明:若存在等价键的元素,则不会构造value_type;否则,构造形式为: +// first four overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) + +// last two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) ``` + +Invalidates pointers and references to elements if a rehashing is issued. + +该接口仅为说明性用途,因为C++不允许在可变参数包之后声明参数`f`。 + +若`Hash::is_transparent`和`Pred::is_transparent`是有效的成员类型别名,则`template`重载版本才会参与重载决议。库假定`Hash`可同时接受`K`与`Key`类型的参数调用,且`Pred`是透明的。这支持异构查找,避免了实例化`Key`类型对象的开销。 + +-- + +--- + +==== try_emplace_and_[c]visit +```c++ template bool try_emplace_and_visit(const key_type& k, Args&&... args, F1&& f1, F2&& f2); template bool try_emplace_and_cvisit(const key_type& k, Args&&... args, F1&& f1, F2&& f2); template bool try_emplace_and_visit(key_type&& k, Args&&... args, F1&& f1, F2&& f2); template bool try_emplace_and_cvisit(key_type&& k, Args&&... args, F1&& f1, F2&& f2); template bool try_emplace_and_visit(K&& k, Args&&... args, F1&& f1, F2&& f2); template bool try_emplace_and_cvisit(K&& k, Args&&... args, F1&& f1, F2&& f2); ``` + +若容器中不存在键为`k`的元素,则将由`k`和`args`构造的元素插入容器,随后以新创建元素的非常量引用调用`f1`; +否则,以等价元素的引用调用`f2`;若使用`*_cvisit`重载版本,则该引用为常量引用。 + +[horizontal] +返回值:插入成功则返回true。 +并发:对当前对象*this进行重哈希时阻塞。 +说明:若存在等价键的元素,则不会构造value_type;否则,构造形式为: +// first four overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) + +// last two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) ``` + +Invalidates pointers and references to elements if a rehashing is issued. + +该接口仅为说明性用途,因为C++不允许在可变参数包之后声明`f1`和`f2`参数。 + +若`Hash::is_transparent`和`Pred::is_transparent`是有效的成员类型别名,则`template`重载版本才会参与重载决议。库假定`Hash`可同时接受`K`与`Key`类型的参数调用,且`Pred`是透明的。这支持异构查找,避免了实例化`Key`类型对象的开销。 + +-- + +--- + +==== insert_or_assign +```c++ template bool insert_or_assign(const key_type& k, M&& obj); template bool insert_or_assign(key_type&& k, M&& obj); template bool insert_or_assign(K&& k, M&& obj); ``` + +向容器插入新元素,或通过赋值给容器内已存值来更新现有元素。 + +若容器中存在键为`k`的元素,则通过`std::forward(obj)`赋值来更新该元素。 + +若不存在该元素,则将其以如下形式添加到容器中: +// first two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(obj))) + +// third overload +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(obj))) ``` + +[horizontal] +返回值:插入成功时返回`true`。 +并发:执行重哈希操作时会阻塞当前对象`*this`。 +说明:若触发重哈希,将使指向元素的指针和引用失效。 +`template` 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为有效成员类型别名时才参与重载决议。库假定`Hash`可同时接受`K`与`Key`类型参数调用,且`Pred`是透明的。这支持异构查找,避免了实例化`Key`类型对象的开销。 + +--- + +==== 擦除 +```c++ size_type erase(const key_type& k); template size_type erase(const K& k); ``` + +若存在键等价于`k`的元素,则删除该元素。 + +[horizontal] +返回值:删除的元素数量(0 或 1)。 +异常:仅当哈希函数`hasher`或键比较函数`key_equal`抛出异常时才会抛出异常。 +说明:仅当`Hash::is_transparent`和`Pred::is_transparent`为有效的成员类型别名时,`template`重载版本才参与重载决议。库假定`Hash`可同时接受`K`与`Key`类型的参数调用,且`Pred`是透明的。该设计支持异构查找,避免了实例化`Key`类型对象的开销。 + +--- + +==== erase_if by Key +```c++ template size_type erase_if(const key_type& k, F f); template size_type erase_if(const K& k, F f); ``` + +若存在键与 `k` 等价的元素 `x`,且 `f(x)` 返回 `true`,则删除该元素。 + +[horizontal] +返回值:删除的元素数量(0 或 1)。 +异常:仅当哈希函数`hasher`、键比较函数`key_equal`或函数`f`抛出异常时才会抛出异常。 +说明:`f`接收元素`x`的非常量引用。 + +仅当`std::is_execution_policy_v>`为`false`时,`template`重载版本才参与重载决议。 + +仅当`Hash::is_transparent`和`Pred::is_transparent`为有效的成员类型别名时,`template`重载版本才参与重载决议。库假定`Hash`可同时接受`K`与`Key`类型的参数调用,且`Pred`是透明的。该设计支持异构查找,避免了实例化`Key`类型对象的开销。 + +--- + +==== erase_if +```c++ template size_type erase_if(F f); ``` + +依次以非常量引用为参数,对容器中的每个元素调用`f`,并删除所有`f`返回`true`的元素。 + +[horizontal] +返回值:被删除的元素数量。 +异常:仅当函数 `f` 抛出异常时才会抛出异常。 + +--- + +==== 并行条件擦除 +```c++ template void erase_if(ExecutionPolicy&& policy, F f); ``` + +依次以非常量引用为参数,对容器中的每个元素调用`f`,并删除所有`f`返回`true`的元素。执行过程将根据指定的执行策略语义进行并行化处理。 + +[horizontal] +异常:根据所使用执行策略的异常处理机制,若`f`内部抛出异常,可能会调用`std::terminate`终止程序。 +说明:仅在支持C++17并行算法的编译器中可用。 + +仅当`std::is_execution_policy_v>`为`true`时,该重载版本参与重载决议。 + +不允许使用无顺序执行策略。 + +--- + +==== 交换 +```c++ void swap(concurrent_flat_map& other) noexcept(boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_swap::value); ``` + +将当前容器的内容与参数容器进行交换。 + +若`Allocator::propagate_on_container_swap`已定义且其值为`true`,则交换两个容器的分配器;否则,使用不相等的分配器进行交换会导致未定义行为。 + +[horizontal] +异常:除非`key_equal`或`hasher`在交换时抛出异常,否则不抛出任何异常。并发:会阻塞当前对象`*this`和参数对象`other`。 + +--- + +==== 清空 +```c++ void clear() noexcept; ``` + +清空容器中的所有元素。 + +[horizontal] +后置条件:容器大小 `size() == 0`,且 `max_load() >= max_load_factor() * bucket_count()` +并发:会阻塞当前对象 `*this`。 + +--- + +==== 合并 +```c++ template size_type merge(concurrent_flat_map& source); template size_type merge(concurrent_flat_map&& source); ``` + +将来源容器`source`中所有**键不存在于当前容器`*this`**中的元素**移动插入**到当前容器,并从`source`中擦除这些元素。 + +[horizontal] +返回值:插入的元素数量。 +并发:会阻塞当前对象 `*this` 和源对象 `source`。 + +--- + +=== 观察器 + +==== get_allocator +``` allocator_type get_allocator() const noexcept; ``` + +[horizontal] +返回值:容器的分配器。 + +--- + +==== 哈希函数 +``` hasher hash_function() const; ``` + +[horizontal] +返回值:容器的哈希函数。 + +--- + +==== key_eq +``` key_equal key_eq() const; ``` + +[horizontal] +返回值:容器的键相等性谓词。 + +--- + +=== 映射操作 + +==== count +```c++ size_type count(const key_type& k) const; template size_type count(const K& k) const; ``` + +[horizontal] +返回值:匹配键 `k` 的元素数量(0 或 1)。 +说明:仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为有效成员类型别名时,`template` 重载版本才参与重载决议。库假定哈希函数可同时作用于 `K` 类型与键类型,且相等谓词是透明的,从而支持异构查找,避免实例化键类型带来的开销。 + +若存在并发插入操作,返回值可能无法精确反映容器执行后的真实状态。 + +--- + +==== 包含 +```c++ bool contains(const key_type& k) const; template bool contains(const K& k) const; ``` + +[horizontal] +返回值:布尔值,表示容器中是否存在键等于 `k` 的元素。 +说明:仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为有效成员类型别名时,`template` 重载版本才参与重载决议。库假定哈希函数可同时作用于 `K` 类型与键类型,且相等谓词是透明的,从而支持异构查找,避免实例化键类型带来的开销。 + +若存在并发插入操作,返回值可能无法精确反映容器执行后的真实状态。 + +--- +=== 桶接口 + +==== bucket_count +```c++ size_type bucket_count() const noexcept; ``` + +[horizontal] +返回值:桶数组的大小。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ float load_factor() const noexcept; ``` + +[horizontal] +返回值:`static_cast(size())/static_cast(bucket_count())`;若 `bucket_count() == 0`,则返回 `0`。 + +--- + +==== max_load_factor(最大负载因子) + +```c++ float max_load_factor() const noexcept; ``` + +[horizontal] +返回值:容器的最大负载因子。 + +--- + +==== 设置最大负载因子 +```c++ void max_load_factor(float z); ``` + +[horizontal] +效果:无任何操作,用户不允许修改此参数。保留该函数是为了与 `boost::unordered_map` 保持兼容。 + +--- + + +==== max_load(最大负载) + +```c++ size_type max_load() const noexcept; ``` + +[horizontal] +返回值:容器在不进行重哈希的前提下可容纳的最大元素数量(假定不会再擦除任何元素)。 +说明:容器在构造、重哈希或清空后,其最大负载量至少为 `max_load_factor() * bucket_count()`。在高负载条件下执行擦除操作时,该数值可能会降低。 +若存在并发插入操作,返回值可能无法精确反映容器执行后的真实状态。 + +--- + +==== 重哈希 +```c++ void rehash(size_type n); ``` + +效果:必要时调整桶数组的大小,使桶数量至少为 `n`,且负载因子小于等于最大负载因子。 +适用场景下,该操作会**增大或缩小**容器的桶数量 `bucket_count()`。 + +当容器大小 `size() == 0` 时,调用 `rehash(0)` 会**释放底层的桶数组内存**。 + +会使指向元素的指针和引用失效,并改变元素的存储顺序。 + +[horizontal] +异常:若抛出异常,函数无任何效果(由容器的哈希函数或比较函数抛出的异常除外)。 +并发:阻塞当前对象 `*this`。 + +==== 保留 +```c++ void reserve(size_type n); ``` + +等价于 `a.rehash(ceil(n / a.max_load_factor()))`。 + +与 `rehash` 功能类似,该函数可用于**增大或缩小**容器的桶数量。 + +会使指向元素的指针和引用失效,并改变元素的存储顺序。 + +[horizontal] +异常:若抛出异常,函数无任何效果(由容器的哈希函数或比较函数抛出的异常除外)。 +并发:阻塞当前对象 `*this`。 + +--- + +=== 统计信息 + +==== get_stats +```c++ stats get_stats() const; ``` + +[horizontal] +返回值:容器截至目前执行的插入与查找操作的统计描述信息。 +说明:仅当**启用统计计算**时,该函数才可用。 + +--- + +==== reset_stats +```c++ void reset_stats() noexcept; ``` + +[horizontal] +效果:将容器维护的内部统计数据归零。 +说明:仅当**启用统计计算**时,该函数才可用。 + +--- + +=== 推导指引 +满足以下任一条件时,推导指引不参与重载决议: + +- 该推导指引包含 `InputIterator` 模板参数,且为此参数推导出的类型不符合输入迭代器的要求。 - 该推导指引包含 `Allocator` 模板参数,且为该参数推导出的类型不符合分配器要求。 - 该推导指引包含 `Hash` 模板参数,且为该参数推导出的类型为整型或符合分配器要求。 - 该推导指引包含 `Pred` 模板参数,且为该参数推导出的类型符合分配器要求。 + +推导指引中的 `size++_++type` 参数类型,指向由该推导指引所推导容器类型的 `size++_++type` 成员类型。其默认值与所选构造函数的默认值一致。 + +==== _iter-value-type_ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-value-type__ = + typename std::iterator_traits::value_type; // exposition only +----- + +==== __iter-key-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-key-type__ = std::remove_const_t< + std::tuple_element_t<0, xref:#concurrent_flat_map_iter_value_type[__iter-value-type__]>>; // exposition only +----- + +==== __iter-mapped-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-mapped-type__ = + std::tuple_element_t<1, xref:#concurrent_flat_map_iter_value_type[__iter-value-type__]>; // exposition only +----- + +==== __iter-to-alloc-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-to-alloc-type__ = std::pair< + std::add_const_t>>, + std::tuple_element_t<1, xref:#concurrent_flat_map_iter_value_type[__iter-value-type__]>>; // exposition only +----- + +=== 相等性比较 + +==== operator +```c++ template bool operator==(const concurrent_flat_map& x, const concurrent_flat_map& y); ``` + +若 `x.size() == y.size()`,且对于 `x` 中的每个元素,`y` 中均存在拥有相同键、值相等(使用 `operator==` 比较值类型)的元素,则返回 `true`。 + +[horizontal] +并发:阻塞 `x` 和 `y`。 +说明:若两个容器的相等判断谓词不一致,行为未定义。 + +--- + +==== operator! +```c++ template bool operator!=(const concurrent_flat_map& x, const concurrent_flat_map& y); ``` + +若`x.size() == y.size()`,且`x`中每个元素在`y`中都存在键相同、值相等(使用`operator==`比较值类型)的元素,则返回`false`。 + +[horizontal] +并发:阻塞 `x` 和 `y`。 +说明:若两个容器的相等判断谓词不一致,行为未定义。 + +--- + +=== 交换 +```c++ template void swap(concurrent_flat_map& x, concurrent_flat_map& y) noexcept(noexcept(x.swap(y))); ``` + +等效于 [listing,subs="+macros,+quotes"] +----- +x.xref:#concurrent_flat_map_swap[swap](y); +----- + +--- + +=== erase_if +```c++ template typename concurrent_flat_map::size_type erase_if(concurrent_flat_map& c, Predicate pred); ``` + +等效于 [listing,subs="+macros,+quotes"] +----- +c.xref:#concurrent_flat_map_erase_if[erase_if](pred); +----- + +=== 序列化 + +`concurrent++_++flat++_++map` 可通过本组件库提供的 API,借助 link:../../../../../serialization/index.html[Boost.Serialization] 进行归档/检索。支持常规归档与 XML 归档两种格式。 + +==== 将concurrent++_++flat++_++map保存到归档 + +将 `concurrent++_++flat++_++map` 容器 `x` 的所有元素保存到归档(XML归档) `ar` 中。 + +[horizontal] +要求:`std::remove_const::type` 和 `std::remove_const::type` 必须是可序列化的(支持 XML 序列化),并且它们支持 Boost.Serialization 的 `save_construct_data` / `load_construct_data` 协议(https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 类型自动支持该协议)。并发:阻塞 `x`。 + +--- + +==== 从归档加载concurrent++_++flat++_++map + +删除 `concurrent++_++flat++_++map` 容器 `x` 中所有已存在的元素,并从归档(XML 归档) `ar` 中插入原始 `concurrent++_++flat++_++map` 容器 `other` 的元素副本,这些副本是从 `ar` 所读取的存储中恢复的。 + +[horizontal] +要求;; `x.key++_++equal()` 需要在功能上等价于 `other.key++_++equal()` 。 并发性;; 阻塞于 `x` 。 diff --git a/doc/modules/ROOT/pages/reference/concurrent_flat_set_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/concurrent_flat_set_zh_Hans.adoc new file mode 100644 index 0000000..d7f4983 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/concurrent_flat_set_zh_Hans.adoc @@ -0,0 +1,1272 @@ +[#concurrent_flat_set] +== 类模板 concurrent++_++flat++_++set + +:idprefix: concurrent_flat_set_ + +`boost::concurrent++_++flat++_++set` —— 一种存储唯一值的哈希表,它支持并发的元素插入、删除、查找及访问操作,且无需外部同步机制。 + +尽管 `boost::concurrent++_++flat++_++set` 具备容器特性,但它并不符合 C{plus}{plus}标准中的 https://en.cppreference.com/w/cpp/named_req/Container[容器] 概念。具体而言,该容器未提供迭代器及相关操作(如 `begin` 、 `end` 等)。元素访问通过用户提供的__访问函数__实现,这些函数被传递至 `concurrent++_++flat++_++set` 操作中,并在其内部以受控方式执行。这种基于访问机制的 API 设计能够有效支持低争用的并发应用场景。 + +`boost::concurrent++_++flat++_++set` 的内部数据结构类似于 `boost::unordered++_++flat++_++set` 。由于其采用开放寻址技术, `value++_++type` 必须满足可移动构造要求,且在重哈希过程中无法保持指针稳定性。 + +=== 概要 + +[listing,subs="+macros,+quotes"] +----- +// #include xref:reference/header_concurrent_flat_set.adoc[``] + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator> + class concurrent_flat_set { + public: + // types + using key_type = Key; + using value_type = Key; + using init_type = Key; + using hasher = Hash; + using key_equal = Pred; + using allocator_type = Allocator; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + using stats = xref:reference/stats.adoc#stats_stats_type[__stats-type__]; // if statistics are xref:concurrent_flat_set_boost_unordered_enable_stats[enabled] + + // constants + static constexpr size_type xref:#concurrent_flat_set_constants[bulk_visit_size] = _implementation-defined_; + + // construct/copy/destroy + xref:#concurrent_flat_set_default_constructor[concurrent_flat_set](); + explicit xref:#concurrent_flat_set_bucket_count_constructor[concurrent_flat_set](size_type n, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + template + xref:#concurrent_flat_set_iterator_range_constructor[concurrent_flat_set](InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#concurrent_flat_set_copy_constructor[concurrent_flat_set](const concurrent_flat_set& other); + xref:#concurrent_flat_set_move_constructor[concurrent_flat_set](concurrent_flat_set&& other); + template + xref:#concurrent_flat_set_iterator_range_constructor_with_allocator[concurrent_flat_set](InputIterator f, InputIterator l,const allocator_type& a); + explicit xref:#concurrent_flat_set_allocator_constructor[concurrent_flat_set](const Allocator& a); + xref:#concurrent_flat_set_copy_constructor_with_allocator[concurrent_flat_set](const concurrent_flat_set& other, const Allocator& a); + xref:#concurrent_flat_set_move_constructor_with_allocator[concurrent_flat_set](concurrent_flat_set&& other, const Allocator& a); + xref:#concurrent_flat_set_move_constructor_from_unordered_flat_set[concurrent_flat_set](unordered_flat_set&& other); + xref:#concurrent_flat_set_initializer_list_constructor[concurrent_flat_set](std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#concurrent_flat_set_bucket_count_constructor_with_allocator[concurrent_flat_set](size_type n, const allocator_type& a); + xref:#concurrent_flat_set_bucket_count_constructor_with_hasher_and_allocator[concurrent_flat_set](size_type n, const hasher& hf, const allocator_type& a); + template + xref:#concurrent_flat_set_iterator_range_constructor_with_bucket_count_and_allocator[concurrent_flat_set](InputIterator f, InputIterator l, size_type n, + const allocator_type& a); + template + xref:#concurrent_flat_set_iterator_range_constructor_with_bucket_count_and_hasher[concurrent_flat_set](InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); + xref:#concurrent_flat_set_initializer_list_constructor_with_allocator[concurrent_flat_set](std::initializer_list il, const allocator_type& a); + xref:#concurrent_flat_set_initializer_list_constructor_with_bucket_count_and_allocator[concurrent_flat_set](std::initializer_list il, size_type n, + const allocator_type& a); + xref:#concurrent_flat_set_initializer_list_constructor_with_bucket_count_and_hasher_and_allocator[concurrent_flat_set](std::initializer_list il, size_type n, const hasher& hf, + const allocator_type& a); + xref:#concurrent_flat_set_destructor[~concurrent_flat_set](); + concurrent_flat_set& xref:#concurrent_flat_set_copy_assignment[operator++=++](const concurrent_flat_set& other); + concurrent_flat_set& xref:#concurrent_flat_set_move_assignment[operator++=++](concurrent_flat_set&& other) + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_move_assignment::value); + concurrent_flat_set& xref:#concurrent_flat_set_initializer_list_assignment[operator++=++](std::initializer_list); + allocator_type xref:#concurrent_flat_set_get_allocator[get_allocator]() const noexcept; + + + // visitation + template size_t xref:#concurrent_flat_set_cvisit[visit](const key_type& k, F f); + template size_t xref:#concurrent_flat_set_cvisit[visit](const key_type& k, F f) const; + template size_t xref:#concurrent_flat_set_cvisit[cvisit](const key_type& k, F f) const; + template size_t xref:#concurrent_flat_set_cvisit[visit](const K& k, F f); + template size_t xref:#concurrent_flat_set_cvisit[visit](const K& k, F f) const; + template size_t xref:#concurrent_flat_set_cvisit[cvisit](const K& k, F f) const; + + template + size_t xref:concurrent_flat_set_bulk_visit[visit](FwdIterator first, FwdIterator last, F f); + template + size_t xref:concurrent_flat_set_bulk_visit[visit](FwdIterator first, FwdIterator last, F f) const; + template + size_t xref:concurrent_flat_set_bulk_visit[cvisit](FwdIterator first, FwdIterator last, F f) const; + + template size_t xref:#concurrent_flat_set_cvisit_all[visit_all](F f); + template size_t xref:#concurrent_flat_set_cvisit_all[visit_all](F f) const; + template size_t xref:#concurrent_flat_set_cvisit_all[cvisit_all](F f) const; + template + void xref:#concurrent_flat_set_parallel_cvisit_all[visit_all](ExecutionPolicy&& policy, F f); + template + void xref:#concurrent_flat_set_parallel_cvisit_all[visit_all](ExecutionPolicy&& policy, F f) const; + template + void xref:#concurrent_flat_set_parallel_cvisit_all[cvisit_all](ExecutionPolicy&& policy, F f) const; + + template bool xref:#concurrent_flat_set_cvisit_while[visit_while](F f); + template bool xref:#concurrent_flat_set_cvisit_while[visit_while](F f) const; + template bool xref:#concurrent_flat_set_cvisit_while[cvisit_while](F f) const; + template + bool xref:#concurrent_flat_set_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f); + template + bool xref:#concurrent_flat_set_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f) const; + template + bool xref:#concurrent_flat_set_parallel_cvisit_while[cvisit_while](ExecutionPolicy&& policy, F f) const; + + // capacity + ++[[nodiscard]]++ bool xref:#concurrent_flat_set_empty[empty]() const noexcept; + size_type xref:#concurrent_flat_set_size[size]() const noexcept; + size_type xref:#concurrent_flat_set_max_size[max_size]() const noexcept; + + // modifiers + template bool xref:#concurrent_flat_set_emplace[emplace](Args&&... args); + bool xref:#concurrent_flat_set_copy_insert[insert](const value_type& obj); + bool xref:#concurrent_flat_set_move_insert[insert](value_type&& obj); + template bool xref:#concurrent_flat_set_transparent_insert[insert](K&& k); + template size_type xref:#concurrent_flat_set_insert_iterator_range[insert](InputIterator first, InputIterator last); + size_type xref:#concurrent_flat_set_insert_initializer_list[insert](std::initializer_list il); + + template bool xref:#concurrent_flat_set_emplace_or_cvisit[emplace_or_visit](Args&&... args, F&& f); + template bool xref:#concurrent_flat_set_emplace_or_cvisit[emplace_or_cvisit](Args&&... args, F&& f); + template bool xref:#concurrent_flat_set_copy_insert_or_cvisit[insert_or_visit](const value_type& obj, F f); + template bool xref:#concurrent_flat_set_copy_insert_or_cvisit[insert_or_cvisit](const value_type& obj, F f); + template bool xref:#concurrent_flat_set_move_insert_or_cvisit[insert_or_visit](value_type&& obj, F f); + template bool xref:#concurrent_flat_set_move_insert_or_cvisit[insert_or_cvisit](value_type&& obj, F f); + template bool xref:#concurrent_flat_set_transparent_insert_or_cvisit[insert_or_visit](K&& k, F f); + template bool xref:#concurrent_flat_set_transparent_insert_or_cvisit[insert_or_cvisit](K&& k, F f); + template + size_type xref:#concurrent_flat_set_insert_iterator_range_or_visit[insert_or_visit](InputIterator first, InputIterator last, F f); + template + size_type xref:#concurrent_flat_set_insert_iterator_range_or_visit[insert_or_cvisit](InputIterator first, InputIterator last, F f); + template size_type xref:#concurrent_flat_set_insert_initializer_list_or_visit[insert_or_visit](std::initializer_list il, F f); + template size_type xref:#concurrent_flat_set_insert_initializer_list_or_visit[insert_or_cvisit](std::initializer_list il, F f); + + template + bool xref:#concurrent_flat_set_emplace_and_cvisit[emplace_and_visit](Args&&... args, F1&& f1, F2&& f2); + template + bool xref:#concurrent_flat_set_emplace_and_cvisit[emplace_and_cvisit](Args&&... args, F1&& f1, F2&& f2); + template bool xref:#concurrent_flat_set_copy_insert_and_cvisit[insert_and_visit](const value_type& obj, F1 f1, F2 f2); + template bool xref:#concurrent_flat_set_copy_insert_and_cvisit[insert_and_cvisit](const value_type& obj, F1 f1, F2 f2); + template bool xref:#concurrent_flat_set_move_insert_and_cvisit[insert_and_visit](value_type&& obj, F1 f1, F2 f2); + template bool xref:#concurrent_flat_set_move_insert_and_cvisit[insert_and_cvisit](value_type&& obj, F1 f1, F2 f2); + template bool xref:#concurrent_flat_set_transparent_insert_and_cvisit[insert_and_visit](K&& k, F1 f1, F2 f2); + template bool xref:#concurrent_flat_set_transparent_insert_and_cvisit[insert_and_cvisit](K&& k, F1 f1, F2 f2); + template + size_type xref:#concurrent_flat_set_insert_iterator_range_and_visit[insert_and_visit](InputIterator first, InputIterator last, F1 f1, F2 f2); + template + size_type xref:#concurrent_flat_set_insert_iterator_range_and_visit[insert_and_cvisit](InputIterator first, InputIterator last, F1 f1, F2 f2); + template + size_type xref:#concurrent_flat_set_insert_initializer_list_and_visit[insert_and_visit](std::initializer_list il, F1 f1, F2 f2); + template + size_type xref:#concurrent_flat_set_insert_initializer_list_and_visit[insert_and_cvisit](std::initializer_list il, F1 f1, F2 f2); + + size_type xref:#concurrent_flat_set_erase[erase](const key_type& k); + template size_type xref:#concurrent_flat_set_erase[erase](const K& k); + + template size_type xref:#concurrent_flat_set_erase_if_by_key[erase_if](const key_type& k, F f); + template size_type xref:#concurrent_flat_set_erase_if_by_key[erase_if](const K& k, F f); + template size_type xref:#concurrent_flat_set_erase_if[erase_if](F f); + template void xref:#concurrent_flat_set_parallel_erase_if[erase_if](ExecutionPolicy&& policy, F f); + + void xref:#concurrent_flat_set_swap[swap](concurrent_flat_set& other) + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_swap::value); + void xref:#concurrent_flat_set_clear[clear]() noexcept; + + template + size_type xref:#concurrent_flat_set_merge[merge](concurrent_flat_set& source); + template + size_type xref:#concurrent_flat_set_merge[merge](concurrent_flat_set&& source); + + // observers + hasher xref:#concurrent_flat_set_hash_function[hash_function]() const; + key_equal xref:#concurrent_flat_set_key_eq[key_eq]() const; + + // set operations + size_type xref:#concurrent_flat_set_count[count](const key_type& k) const; + template + size_type xref:#concurrent_flat_set_count[count](const K& k) const; + bool xref:#concurrent_flat_set_contains[contains](const key_type& k) const; + template + bool xref:#concurrent_flat_set_contains[contains](const K& k) const; + + // bucket interface + size_type xref:#concurrent_flat_set_bucket_count[bucket_count]() const noexcept; + + // hash policy + float xref:#concurrent_flat_set_load_factor[load_factor]() const noexcept; + float xref:#concurrent_flat_set_max_load_factor[max_load_factor]() const noexcept; + void xref:#concurrent_flat_set_set_max_load_factor[max_load_factor](float z); + size_type xref:#concurrent_flat_set_max_load[max_load]() const noexcept; + void xref:#concurrent_flat_set_rehash[rehash](size_type n); + void xref:#concurrent_flat_set_reserve[reserve](size_type n); + + // statistics (if xref:concurrent_flat_set_boost_unordered_enable_stats[enabled]) + stats xref:#concurrent_flat_set_get_stats[get_stats]() const; + void xref:#concurrent_flat_set_reset_stats[reset_stats]() noexcept; + }; + + // Deduction Guides + template>, + class Pred = std::equal_to>, + class Allocator = std::allocator>> + concurrent_flat_set(InputIterator, InputIterator, typename xref:#concurrent_flat_set_deduction_guides[__see below__]::size_type = xref:#concurrent_flat_set_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> concurrent_flat_set, Hash, Pred, Allocator>; + + template, class Pred = std::equal_to, + class Allocator = std::allocator> + concurrent_flat_set(std::initializer_list, typename xref:#concurrent_flat_set_deduction_guides[__see below__]::size_type = xref:#concurrent_flat_set_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> concurrent_flat_set; + + template + concurrent_flat_set(InputIterator, InputIterator, typename xref:#concurrent_flat_set_deduction_guides[__see below__]::size_type, Allocator) + -> concurrent_flat_set, + boost::hash>, + std::equal_to>, Allocator>; + + template + concurrent_flat_set(InputIterator, InputIterator, Allocator) + -> concurrent_flat_set, + boost::hash>, + std::equal_to>, Allocator>; + + template + concurrent_flat_set(InputIterator, InputIterator, typename xref:#concurrent_flat_set_deduction_guides[__see below__]::size_type, Hash, + Allocator) + -> concurrent_flat_set, Hash, + std::equal_to>, Allocator>; + + template + concurrent_flat_set(std::initializer_list, typename xref:#concurrent_flat_set_deduction_guides[__see below__]::size_type, Allocator) + -> concurrent_flat_set, std::equal_to, Allocator>; + + template + concurrent_flat_set(std::initializer_list, Allocator) + -> concurrent_flat_set, std::equal_to, Allocator>; + + template + concurrent_flat_set(std::initializer_list, typename xref:#concurrent_flat_set_deduction_guides[__see below__]::size_type, Hash, Allocator) + -> concurrent_flat_set, Allocator>; + +} // namespace unordered +} // namespace boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_Key_ +|`Key` must be https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^] into the container +https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入] 到容器中的要求,且需满足从容器中 https://en.cppreference.com/w/cpp/named_req/Erasable[可擦除] 的要求。 + +|_Hash_ +|A unary function object type that acts a hash function for a `Key`. It takes a single argument of type `Key` and returns a value of type `std::size_t`. + +|_Pred_ +|A binary function object that induces an equivalence relation on values of type `Key`. It takes two arguments of type `Key` and returns a value of type `bool`. + +|_Allocator_ +|An allocator whose value type is the same as the table's value type. +`std::allocator_traits::pointer` 与 `std::allocator_traits::const_pointer` 必须分别可与 `value_type*` 和 `const value_type*` 相互转换。 + +|=== + +容器的元素存储在内部的桶数组中。元素根据其哈希值被插入到对应的桶中,若该桶已被占用(即发生哈希冲突),则使用原位置附近的可用桶。 + +调用 `insert`/`emplace` 或执行 `rehash`/`reserve` 操作时,桶数组的大小会自动扩容。容器的负载因子(元素数量除以桶数量)永远不会超过 `max_load_factor()`,仅在尺寸较小时,实现可能允许更高的负载因子。 + +若 `link:../../../../../container_hash/doc/html/hash.html#ref_hash_is_avalanchinghash[hash_is_avalanching]::value` 为 `true`,则哈希函数直接使用;否则会添加一个位混合后处理阶段,以额外的计算开销为代价提升哈希质量。 + +--- + +=== 并发要求与保证 + +要求对同一 `Hash` 或 `Pred` 常量实例并发调用 `operator()` 时不得引入数据竞争。对于 `Alloc` (即 `Allocator` 或其重绑定后的任意分配器类型),在同一实例 `al` 上并发调用以下操作时不得引入数据竞争: + +* 通过从`Alloc`重新绑定的分配器以`al`进行拷贝构造 +* `std::allocator_traits::allocate` +* `std::allocator_traits::deallocate` +* `std::allocator_traits::construct` +* `std::allocator_traits::destroy` + +通常而言,若 `Hash` 、 `Pred` 和 `Allocator` 这些类型不包含状态,或其操作仅涉及对内部数据成员的常量访问,即可满足上述要求。 + +除析构操作外,对同一个 `concurrent++_++flat++_++set` 实例并发调用任何操作都不会引发数据竞争——即这些操作是线程安全的。 + +若操作 *op* 显式指定为__阻塞于__ 容器 `x` (其中 `x` 是 `boost::concurrent++_++flat++_++set` 的实例),则先前对 `x` 的阻塞操作将与 *op* 同步。因此,在多线程场景下,对同一 `concurrent++_++flat++_++set` 的阻塞操作将按顺序执行。 + +若某个操作仅在触发内部重哈希时才会阻塞于 _`x`_,则称该操作__阻塞于 _`x`_ 的重哈希过程__。 + +当由 `boost::concurrent++_++flat++_++set` 内部执行时,用户提供的访问函数对传入元素的以下操作不会引发数据竞争: + +* 对元素的读取访问。 +* 对元素的非可变修改。 +* 对元素的可变修改: +** 在容器接受两个访问函数的操作中,此条件始终适用于第一个访问函数。 ** 在名称不包含 `cvisit` 的非常量容器函数中,此条件适用于最后一个(或唯一一个)访问函数。 + +任何插入或修改元素 `e` 的 `boost::concurrent++_++flat++_++set` 操作都会与在 `e` 的内部调用的访问函数同步。 + +由 `boost::concurrent++_++flat++_++set` `x` 执行的访问函数不允许调用 `x` 上的任何操作;若并发未完成操作不直接或间接访问 `x` ,则允许调用不同 `boost::concurrent++_++flat++_++set` 实例 `y` 上的操作。 + +--- + +=== 配置宏 + +==== `BOOST++_++UNORDERED++_++DISABLE++_++REENTRANCY++_++CHECK` + +在调试版本中(更准确地说,当未定义 link:../../../../../assert/doc/html/assert.html#boost_assert_is_void[`BOOST++_++ASSERT++_++IS++_++VOID`] 时),系统会检测__容器重入__行为(即在访问 `m` 元素的函数内部非法调用 `m` 上的操作),并通过 `BOOST++_++ASSERT++_++MSG` 发出信号。若需关注运行时速度,可通过全局定义此宏来禁用该功能。 + +--- + +==== `BOOST_UNORDERED_ENABLE_STATS` + +全局定义此宏以启用容器的 xref:reference/stats.adoc#stats[统计计算] 功能。请注意,此选项会降低多数操作的总体性能。 + +--- + +=== 常量 + +```cpp static constexpr size_type bulk_visit_size; ``` + +xref:concurrent_flat_set_bulk_visit[批量访问]操作内部使用的块大小。 + +=== 构造函数 + +==== 默认构造函数 +```c++ concurrent_flat_set(); ``` + +构造一个空容器,使用`hasher()`作为哈希函数,`key_equal()`作为键相等谓词,`allocator_type()`作为分配器。 + +[horizontal] +后置条件:`size() == 0` 要求:若使用默认参数,则 `hasher`、`key_equal` 和 `allocator_type` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 桶数构造函数 +```c++ explicit concurrent_flat_set(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); ``` + +构造一个拥有至少`n`个桶的空容器,使用`hf`作为哈希函数,`eql`作为键相等谓词,`a`作为分配器。 + +[horizontal] +后置条件:`size() == 0` 要求:若使用默认参数,则 `hasher`、`key_equal` 和 `allocator_type` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + concurrent_flat_set(InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `eql` 作为键相等性谓词、 `a` 作为分配器,并将 `++[++f, l)` 范围内的元素插入其中。 + +[horizontal] +要求;; 若使用默认值,则 `hasher` 、 `key++_++equal` 和 `allocator++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 复制构造函数 +```c++ concurrent_flat_set(concurrent_flat_set const& other); ``` + +拷贝构造函数。拷贝容器内的元素、哈希函数、相等谓词及分配器。 + +若`Allocator::select_on_container_copy_construction`存在且签名正确,则分配器将根据其返回值构造。 + +[horizontal] +要求:`value_type` 可复制构造。并发:阻塞 `other`。 + +--- + +==== 移动构造函数 +```c++ concurrent_flat_set(concurrent_flat_set&& other); ``` + +移动构造函数。`other` 的内部桶数组将直接转移至新容器。哈希函数、谓词及分配器均从 `other` 移动构造。若统计功能已通过 xref:concurrent_flat_set_boost_unordered_enable_stats[启用],则从 `other` 转移内部统计信息并调用 `other.reset_stats()`。 + +[horizontal] +并发说明:;; 对 `other` 产生阻塞 + +--- + +==== 带分配器的迭代器范围构造函数 +```c++ template concurrent_flat_set(InputIterator f, InputIterator l, const allocator_type& a); ``` + +构造一个以`a`为分配器、使用默认哈希函数与键相等谓词的空容器,并将`[f, l)`范围内的元素插入其中。 + +[horizontal] +要求:`hasher`、`key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 分配器构造函数 +```c++ explicit concurrent_flat_set(Allocator const& a); ``` + +构造一个使用分配器`a`的空容器。 + +--- + +==== 带分配器的复制构造函数 +```c++ concurrent_flat_set(concurrent_flat_set const& other, Allocator const& a); ``` + +构造一个容器,复制 `other` 中的元素、哈希函数和谓词,但使用分配器 `a`。 + +[horizontal] +并发说明:;; 对 `other` 产生阻塞 + +--- + +==== 带分配器的移动构造函数 +```c++ concurrent_flat_set(concurrent_flat_set&& other, Allocator const& a); ``` + +若`a == other.get_allocator()`,则`other`的元素将直接转移至新容器;否则,元素从`other`的元素移动构造而来。哈希函数与谓词从`other`移动构造,分配器从`a`拷贝构造。若统计功能已通过 xref:concurrent_flat_set_boost_unordered_enable_stats[enabled],仅当`a == other.get_allocator()`时从`other`转移内部统计信息,且始终调用`other.reset_stats()`。 + +[horizontal] +并发说明:;; 对 `other` 产生阻塞 + +--- + +==== 从 unordered++_++flat++_++set 的移动构造函数 + +```c++ concurrent_flat_set(unordered_flat_set&& other); ``` + +通过 xref:#unordered_flat_set[`unordered_flat_set`] 移动构造。`other` 的内部桶数组直接转移至新容器。哈希函数、谓词及分配器均从 `other` 移动构造。若统计功能已通过 xref:concurrent_flat_set_boost_unordered_enable_stats[enabled],则从 `other` 转移内部统计信息并调用 `other.reset_stats()`。 + +[horizontal] +Complexity:;; O(`bucket_count()`) + +--- + +==== 初始化列表构造函数 +[source,c++,subs="+quotes"] +---- +concurrent_flat_set(std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `eql` 作为键相等性谓词、 `a` 作为分配器,并 `il` 中的元素插入其中。 + +[horizontal] +要求;; 若使用默认值,则 `hasher` 、 `key++_++equal` 和 `allocator++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带分配器的桶数构造函数 +```c++ concurrent_flat_set(size_type n, allocator_type const& a); ``` + +构造一个至少包含 `n` 个桶的空容器,以 `hf` 作为哈希函数,使用默认键相等谓词,并以 `a` 作为分配器。 + +[horizontal] +后置条件:`size() == 0` 要求:`hasher` 和 `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 带哈希函数和分配器的桶数构造函数 +```c++ concurrent_flat_set(size_type n, hasher const& hf, allocator_type const& a); ``` + +构造一个至少包含 `n` 个桶的空容器,以 `hf` 作为哈希函数,使用默认键相等谓词,并以 `a` 作为分配器。 + +[horizontal] +后置条件:`size() == 0` 要求:`key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 带桶数和分配器的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + concurrent_flat_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 作为分配器以及默认的哈希函数和键相等性谓词,并将 `++[++f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:`hasher`、`key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 带桶数和哈希函数的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- + template + concurrent_flat_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `a` 作为分配器以及默认的键相等性谓词,并将 `++[++f, l)` 范围内的元素插入其中。 + +[horizontal] +要求;; `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带分配器的初始化列表构造函数 + +```c++ concurrent_flat_set(std::initializer_list il, const allocator_type& a); ``` + +构造一个使用分配器`a`、默认哈希函数和键相等谓词的空容器,并将`il`中的元素插入其中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 带桶数和分配器的初始化列表构造函数 + +```c++ concurrent_flat_set(std::initializer_list il, size_type n, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用分配器 `a` 以及默认哈希函数和键相等谓词,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 带桶数、哈希函数和分配器的初始化列表构造函数 + +```c++ concurrent_flat_set(std::initializer_list il, size_type n, const hasher& hf, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,以 `hf` 作为哈希函数、`a` 作为分配器,使用默认键相等谓词,并将 `il` 中的元素插入其中。 + +[horizontal] +要求;; `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +=== 析构函数 + +```c++ ~concurrent_flat_set(); ``` + +[horizontal] +注意:;; 析构函数会作用于所有元素,且所有内存都会被释放 + +--- + +=== 赋值操作 + +==== 复制赋值 + +```c++ concurrent_flat_set& operator=(concurrent_flat_set const& other); ``` + +赋值运算符。销毁先前已存在的元素,从 `other` 拷贝赋值哈希函数和谓词;若 `Alloc::propagate_on_container_copy_assignment` 存在且其值为 `true`,则从 `other` 拷贝赋值分配器;最后插入 `other` 元素的副本。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入^] 要求。并发:阻塞 `*this` 和 `other`。 + +--- + +==== 移动赋值 +```c++ concurrent_flat_set& operator=(concurrent_flat_set&& other) noexcept(boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_move_assignment::value); ``` +移动赋值运算符。销毁先前已存在的元素,交换当前对象与 other 的哈希函数和谓词;若 Alloc::propagate_on_container_move_assignment 存在且其值为 true,则从 other 移动赋值分配器。若此时分配器与 other.get_allocator() 相等,other 的内部桶数组将直接转移给 *this;否则,插入由 other 元素移动构造的副本。若统计功能已通过 xref:concurrent_flat_set_boost_unordered_enable_stats [enabled],仅当最终分配器与 other.get_allocator() 相等时,从 other 转移内部统计信息,且始终调用 other.reset_stats()。 + +[horizontal] +并发特性:;; 阻塞 `*this` 与 `other` + +--- + +==== 初始化列表赋值 +```c++ concurrent_flat_set& operator=(std::initializer_list il); ``` + +通过初始化列表中的值进行赋值。先前存在的所有元素都会被销毁。 + +[horizontal] +前置要求:;; `value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^] +并发特性:;; 阻塞 `*this` + +--- + +=== 访问操作 + +==== [c]visit + +```c++ template size_t visit(const key_type& k, F f); template size_t visit(const key_type& k, F f) const; template size_t cvisit(const key_type& k, F f) const; template size_t visit(const K& k, F f); template size_t visit(const K& k, F f) const; template size_t cvisit(const K& k, F f) const; ``` + +若存在键与 `k` 等价的元素 `x`,则以指向 `x` 的常量引用调用函数 `f`。 + +[horizontal] +返回值:;; 访问到的元素数量(0 或 1)。 +注意:;; 仅当 `Hash::is_transparent` 与 `Pred::is_transparent` 为合法成员别名时,`template` 重载版本才会参与重载决议。库假定 `Hash` 可同时接收 `K` 与 `Key` 类型调用,且 `Pred` 是透明的。这支持异构查找,避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 批量访问 + +```c++ template size_t visit(FwdIterator first, FwdIterator last, F f); template size_t visit(FwdIterator first, FwdIterator last, F f) const; template size_t cvisit(FwdIterator first, FwdIterator last, F f) const; ``` + +对范围 `[first, last)` 中的每个元素 `k`,若容器中存在键与 `k` 等价的元素 `x`,则以指向 `x` 的常量引用调用函数 `f`。 + +尽管功能上等同于对每个键单独调用 `[c]visit`,但得益于内部的流式优化,批量访问通常性能更高。建议当 `std::distance(first,last)` 至少达到 `bulk_visit_size` 时使用批量访问以获得性能提升;超过该大小后,性能不会进一步提升。 + +[horizontal] +前置要求:;; `FwdIterator` 是一个传统前向迭代器(C++11 至 C++17),或满足 `std::forward_iterator` 要求(C++20 及更高版本)。 +对于 `K = std::iterator_traits::value_type`,要么 `K` 是 `key_type`,要么 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员别名。 +在后一种情况下,库假定 `Hash` 可同时接收 `K` 与 `Key` 类型调用,且 `Pred` 是透明的。这支持异构查找,避免了实例化 `Key` 类型对象的开销。 +返回值:;; 访问到的元素数量。 + +--- + +==== [c]visit_all + +```c++ template size_t visit_all(F f); template size_t visit_all(F f) const; template size_t cvisit_all(F f) const; ``` + +依次以指向哈希表中每个元素的常量引用调用函数 `f`。 + +[horizontal] +返回值:;; 访问到的元素总数。 + +--- + +==== 并行 [c]visit_all + +```c++ template void visit_all(ExecutionPolicy&& policy, F f); template void visit_all(ExecutionPolicy&& policy, F f) const; template void cvisit_all(ExecutionPolicy&& policy, F f) const; ``` + +以指向哈希表中每个元素的常量引用调用函数 `f`。执行过程会根据指定执行策略的语义进行并行化。 + +[horizontal] +抛出异常:;; 根据所使用执行策略的异常处理机制,若 `f` 内部抛出异常,则可能调用 `std::terminate`。 +注意:;; 仅在支持 C++17 并行算法的编译器中可用。 +仅当 `std::is_execution_policy_v>` 为 `true` 时,这些重载版本才会参与重载决议。 +不允许使用无序执行策略。 + +--- + +==== [c]visit_while + +```c++ template bool visit_while(F f); template bool visit_while(F f) const; template bool cvisit_while(F f) const; ``` + +依次以指向哈希表中每个元素的常量引用调用函数 `f`,直到 `f` 返回 `false` 或遍历完所有元素。 + +[horizontal] +返回值:;; 当且仅当 `f` 返回过 `false` 时,返回 `false`。 + +--- + +==== 并行 ++[++c++]++visit++_++while + +```c++ template bool visit_while(ExecutionPolicy&& policy, F f); template bool visit_while(ExecutionPolicy&& policy, F f) const; template bool cvisit_while(ExecutionPolicy&& policy, F f) const; ``` + +以指向哈希表中每个元素的常量引用调用函数 `f`,直到 `f` 返回 `false` 或遍历完所有元素。执行过程会根据指定执行策略的语义进行并行化。 + +[horizontal] +返回值:;; 当且仅当 `f` 返回过 `false` 时,返回 `false`。 +抛出异常:;; 根据所使用执行策略的异常处理机制,若 `f` 内部抛出异常,则可能调用 `std::terminate`。 +注意:;; 仅在支持 C++17 并行算法的编译器中可用。 +仅当 `std::is_execution_policy_v>` 为 `true` 时,这些重载版本才会参与重载决议。 +不允许使用无序执行策略。 +并行化意味着执行流程不会在 `f` 返回 `false` 时立即终止,因此 `f` 可能还会被后续元素调用并同样返回 `false`。 + +--- + +=== 大小与容量 + +==== 空 + +```c++ [[nodiscard]] bool empty() const noexcept; ``` + +[horizontal] +返回值:;; `size() == 0` + +--- + +==== 大小 + +```c++ size_type size() const noexcept; ``` + +[horizontal] +返回值:;; 哈希表中的元素总数。 + +[horizontal] +注意:;; 在存在并发插入操作时,返回的值可能无法准确反映函数执行后哈希表的真实大小。 + +--- + +==== max_size + +```c++ size_type max_size() const noexcept; ``` + +[horizontal] +返回值:;; 哈希表能容纳的最大元素数量(最大容量)。 + +--- + +=== 修改器 + +==== 原地构造 +```c++ template bool emplace(Args&&... args); ``` + +当且仅当哈希表中不存在等价键的元素时,才会使用参数 `args` 构造对象并插入到哈希表中。 + +[horizontal] +前置要求:;; `value_type` 可由参数 `args` 构造。 +返回值:;; 成功插入元素时返回 `true`。 +并发特性:;; 若触发重哈希,则会阻塞当前对象 `*this`。 +注意:;; 若执行重哈希,将使指向元素的指针和引用失效。 + +--- + +==== 复制插入 +```c++ bool insert(const value_type& obj); ``` + +当且仅当哈希表中不存在等价键的元素时,才将 `obj` 插入到哈希表中。 + +[horizontal] +前置要求:;; `value_type` 满足可复制插入要求。 +返回值:;; 成功插入元素时返回 `true`。 +并发特性:;; 若触发重哈希,则会阻塞当前对象 `*this`。 +注意:;; 若执行重哈希,将使指向元素的指针和引用失效。 + +--- + +==== 移动插入 +```c++ bool insert(value_type&& obj); ``` + +当且仅当哈希表中不存在等价键的元素时,才将 `obj` 插入到哈希表中。 + +[horizontal] +前置要求:;; `value_type` 满足可移动插入要求。 +返回值:;; 成功插入元素时返回 `true`。 +并发特性:;; 若触发重哈希,则会阻塞当前对象 `*this`。 +注意:;; 若执行重哈希,将使指向元素的指针和引用失效。 + +--- + +==== 透明插入 +```c++ template bool insert(K&& k); ``` + +当且仅当容器中不存在等价键的元素时,才会使用 `std::forward(k)` 构造元素并插入到容器中。 + +[horizontal] +前置要求:;; `value_type` 可通过 `k` 原位构造。 +返回值:;; 成功插入元素时返回 `true`。 +并发特性:;; 若触发重哈希,则会阻塞当前对象 `*this`。 +注意:;; 若执行重哈希,将使指向元素的指针和引用失效。 +仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员别名时,该重载版本才参与重载决议。 +库假定 `Hash` 可同时接收 `K` 与 `Key` 类型调用,且 `Pred` 是透明的。这支持异构查找,避免实例化 `Key` 类型对象的开销。 + +--- + +==== 迭代器范围插入 +```c++ template size_type insert(InputIterator first, InputIterator last); ``` + +等效于 [listing,subs="+macros,+quotes"] +----- + while(first != last) this->xref:#concurrent_flat_set_emplace[emplace](*first++); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== 初始化列表插入 +```c++ size_type insert(std::initializer_list il); ``` + +等效于 [listing,subs="+macros,+quotes"] +----- + this->xref:#concurrent_flat_set_insert_iterator_range[insert](il.begin(), il.end()); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== emplace_or_[c]visit +```c++ template bool emplace_or_visit(Args&&... args, F&& f); template bool emplace_or_cvisit(Args&&... args, F&& f); ``` + +若哈希表中不存在等价键的元素,则使用参数 `args` 构造对象并插入表中;否则,以等价元素的常量引用为参数调用函数 `f`。 + +[horizontal] +前置要求:;; `value_type` 可由参数 `args` 构造。 +返回值:;; 成功插入元素时返回 `true`。 +并发特性:;; 若触发重哈希,则会阻塞当前对象 `*this`。 +注意:;; 若执行重哈希,将使指向元素的指针和引用失效。 +该接口仅为说明性设计,因为 C++ 不允许在可变参数包之后声明参数 `f`。 + +--- + +==== 复制 insert++_++or++_[++c++]++visit +```c++ template bool insert_or_visit(const value_type& obj, F f); template bool insert_or_cvisit(const value_type& obj, F f); ``` + +当且仅当哈希表中不存在等价键的元素时,才将 `obj` 插入表中;否则,以等价元素的常量引用为参数调用函数 `f`。 + +[horizontal] +前置要求:;; `value_type` 满足可复制插入要求。 +返回值:;; 成功插入元素时返回 `true`。 +并发特性:;; 若触发重哈希,则会阻塞当前对象 `*this`。 +注意:;; 若执行重哈希,将使指向元素的指针和引用失效。 + +--- + +==== 移动 insert++_++or++_[++c++]++visit +```c++ template bool insert_or_visit(value_type&& obj, F f); template bool insert_or_cvisit(value_type&& obj, F f); ``` + +当且仅当哈希表中不存在等价键的元素时,才将 `obj` 插入表中;否则,以等价元素的常量引用为参数调用函数 `f`。 + +[horizontal] +前置要求:;; `value_type` 满足可移动插入要求。 +返回值:;; 成功插入元素时返回 `true`。 +并发特性:;; 若触发重哈希,则会阻塞当前对象 `*this`。 +注意:;; 若执行重哈希,将使指向元素的指针和引用失效。 + +--- + +==== 透明 insert++_++or++_[++c++]++visit +```c++ template bool insert_or_visit(K&& k, F f); template bool insert_or_cvisit(K&& k, F f); ``` + +当且仅当容器中不存在等价键的元素时,才会使用 `std::forward(k)` 构造元素并插入容器;否则,以等价元素的常量引用为参数调用函数 `f`。 + +[horizontal] +前置要求:;; `value_type` 可通过 `k` 原位构造。 +返回值:;; 成功插入元素时返回 `true`。 +并发特性:;; 若触发重哈希,则会阻塞当前对象 `*this`。 +注意:;; 若执行重哈希,将使指向元素的指针和引用失效。 +仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员别名时,该组重载才参与重载决议。 +库假定 `Hash` 可同时接收 `K` 与 `Key` 类型调用,且 `Pred` 是透明的。这支持异构查找,避免实例化 `Key` 类型对象的开销。 + +--- + +==== 迭代器范围插入或访问 +```c++ template size_type insert_or_visit(InputIterator first, InputIterator last, F f); template size_type insert_or_cvisit(InputIterator first, InputIterator last, F f); ``` + +等效于 [listing,subs="+macros,+quotes"] +----- + while(first != last) this->xref:#concurrent_flat_set_emplace_or_cvisit[emplace_or_[c\]visit](*first++, f); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== 初始化列表插入或访问 +```c++ template size_type insert_or_visit(std::initializer_list il, F f); template size_type insert_or_cvisit(std::initializer_list il, F f); ``` + +等效于 [listing,subs="+macros,+quotes"] +----- + this->xref:#concurrent_flat_set_insert_iterator_range_or_visit[insert_or_[c\]visit](il.begin(), il.end(), std::ref(f)); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== emplace_and_[c]visit +```c++ template bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2); template bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2); ``` + +若哈希表中不存在等价键的元素,则使用参数 `args` 构造对象并插入表中,随后以新创建元素的常量引用为参数调用函数 `f1`;否则,以等价元素的常量引用为参数调用函数 `f2`。 + +[horizontal] +前置要求:;; `value_type` 可由参数 `args` 构造。 +返回值:;; 成功插入元素时返回 `true`。 +并发特性:;; 若触发重哈希,则会阻塞当前对象 `*this`。 +注意:;; 若执行重哈希,将使指向元素的指针和引用失效。 +该接口仅为说明性设计,因为 C++ 不允许在可变参数包之后声明参数 `f1` 和 `f2`。 + +--- + +==== 复制 insert++_++and++_[++c++]++visit +```c++ template bool insert_and_visit(const value_type& obj, F1 f1, F2 f2); template bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2); ``` + +当且仅当哈希表中不存在等价键的元素时,将 `obj` 插入表中,随后以新创建元素的常量引用为参数调用函数 `f1`;否则,以等价元素的常量引用为参数调用函数 `f2`。 + +[horizontal] +前置要求:;; `value_type` 满足可复制插入要求。 +返回值:;; 成功插入元素时返回 `true`。 +并发特性:;; 若触发重哈希,则会阻塞当前对象 `*this`。 +注意:;; 若执行重哈希,将使指向元素的指针和引用失效。 + +--- + +==== 移动 insert++_++and++_[++c++]++visit +```c++ template bool insert_and_visit(value_type&& obj, F1 f1, F2 f2); template bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2); ``` + +当且仅当哈希表中不存在等价键的元素时,将 `obj` 插入表中,随后以新创建元素的常量引用为参数调用函数 `f1`;否则,以等价元素的常量引用为参数调用函数 `f2`。 + +[horizontal] +前置要求:;; `value_type` 满足可移动插入要求。 +返回值:;; 成功插入元素时返回 `true`。 +并发特性:;; 若触发重哈希,则会阻塞当前对象 `*this`。 +注意:;; 若执行重哈希,将使指向元素的指针和引用失效。 + +--- + +==== 透明 insert++_++and++_[++c++]++visit(透明插入并 ++[++c++]++ 访问) +```c++ template bool insert_and_visit(K&& k, F1 f1, F2 f2); template bool insert_and_cvisit(K&& k, F1 f1, F2 f2); ``` + +当且仅当容器中不存在键等价的元素时,将通过`std::forward(k)`构造的元素插入容器,随后使用新建元素的常量引用调用`f1`;否则使用等价元素的常量引用调用`f2`。 + +[horizontal] +前置要求:;; `value_type` 可通过 `k` 原位构造。 +返回值:;; 成功插入元素时返回 `true`。 +并发特性:;; 若触发重哈希,则会阻塞当前对象 `*this`。 +注意:;; 若执行重哈希,将使指向元素的指针和引用失效。 +仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员别名时,该组重载才参与重载决议。 +库假定 `Hash` 可同时接收 `K` 与 `Key` 类型调用,且 `Pred` 是透明的。这支持异构查找,避免实例化 `Key` 类型对象的开销。 + +--- + +==== 迭代器范围插入并访问 +```c++ template size_type insert_and_visit(InputIterator first, InputIterator last, F1 f1, F2 f2); template size_type insert_and_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2); ``` + +等效于 [listing,subs="+macros,+quotes"] +----- + while(first != last) this->xref:#concurrent_flat_set_emplace_and_cvisit[emplace_and_[c\]visit](*first++, f1, f2); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== 初始化列表插入并访问 +```c++ template size_type insert_and_visit(std::initializer_list il, F1 f1, F2 f2); template size_type insert_and_cvisit(std::initializer_list il, F1 f1, F2 f2); ``` + +等效于 [listing,subs="+macros,+quotes"] +----- + this->xref:#concurrent_flat_set_insert_iterator_range_and_visit[insert_and_[c\]visit](il.begin(), il.end(), std::ref(f1), std::ref(f2)); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== 擦除 +```c++ size_type erase(const key_type& k); template size_type erase(const K& k); ``` + +若存在键与`k`等价的元素,则将其删除。 + +[horizontal] +返回值:;; 删除的元素数量(0 或 1)。 +抛出:;; 仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出异常。 +备注:;; `template` 重载仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为合法成员类型别名时才参与重载决议。标准库假定 `Hash` 可同时接受 `K` 与 `Key` 类型调用,且 `Pred` 是透明的。这实现了异构查找,避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 通过键进行条件擦除 +```c++ template size_type erase_if(const key_type& k, F f); template size_type erase_if(const K& k, F f); ``` + +若键与`k`等价的元素存在且`f(x)`为`true`,则删除该元素`x`。 + +[horizontal] +返回值:;; 删除的元素数量(0 或 1)。 +抛出:;; 仅当 `hasher`、`key_equal` 或 `f` 抛出异常时才会抛出异常。 +备注:;; 仅当 `std::is_execution_policy_v>` 为 `false` 时,`template` 重载才参与重载决议。 + +仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为合法成员类型别名时,`template` 重载才参与重载决议。标准库假定 `Hash` 可同时接受 `K` 与 `Key` 类型调用,且 `Pred` 是透明的。这实现了异构查找,避免了实例化 `Key` 类型对象的开销。 + +--- + +==== erase++_++if +```c++ template size_type erase_if(F f); ``` + +依次以表中每个元素的引用调用`f`,并删除`f`返回`true`的元素。 + +[horizontal] +返回值:;; 删除的元素数量。 +抛出:;; 仅当 `f` 抛出异常时才会抛出异常。 + +--- + +==== 并行条件擦除 +```c++ template void erase_if(ExecutionPolicy&& policy, F f); ``` + +以表中每个元素的引用调用`f`,并删除`f`返回`true`的元素。执行过程将根据指定执行策略的语义进行并行化。 + +[horizontal] +抛出:;; 根据所使用执行策略的异常处理机制,若`f`内抛出异常,则可能调用`std::terminate`。 +备注:;; 仅在支持C++17并行算法的编译器中可用。 + +该重载仅当`std::is_execution_policy_v>`为`true`时才参与重载决议。 + +不允许使用无顺序执行策略。 + +--- + +==== 交换 +```c++ void swap(concurrent_flat_set& other) noexcept(boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_swap::value); ``` + +交换当前表与参数表的内容。 + +若`Allocator::propagate_on_container_swap`已声明且`Allocator::propagate_on_container_swap::value`为`true`,则交换两个表的分配器。否则,使用不相等的分配器进行交换会导致未定义行为。 + +[horizontal] +抛出:;; 除非`key_equal`或`hasher`在交换时抛出异常,否则不抛出任何异常。 +并发:;; 阻塞`*this`和`other`。 + +--- + +==== 清空 +```c++ void clear() noexcept; ``` + +清空表中的所有元素。 + +[horizontal] +后置条件:;; `size() == 0`,`max_load() >= max_load_factor() * bucket_count()` +并发:;; 阻塞`*this`。 + +--- + +==== 合并 +```c++ template size_type merge(concurrent_flat_set& source); template size_type merge(concurrent_flat_set&& source); ``` + +将`source`中所有键尚未存在于`*this`中的元素移动插入,并从`source`中删除这些元素。 + +[horizontal] +返回值:;; 插入的元素数量。 +并发:;; 阻塞`*this`和`source`。 + +--- + +=== 观察器 + +==== get_allocator +``` allocator_type get_allocator() const noexcept; ``` + +[horizontal] +返回值:;; 表的分配器。 + +--- + +==== 哈希函数 +``` hasher hash_function() const; ``` + +[horizontal] +返回值:;; 表的哈希函数。 + +--- + +==== key_eq +``` key_equal key_eq() const; ``` + +[horizontal] +返回值:;; 表的键相等性断言。 + +--- + +=== 集合操作 + +==== count +```c++ size_type count(const key_type& k) const; template size_type count(const K& k) const; ``` + +[horizontal] +返回值:;; 键与`k`等价的元素数量(0 或 1)。 +备注:;; `template` 重载仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为合法成员类型别名时才参与重载决议。标准库假定 `Hash` 可同时接受 `K` 与 `Key` 类型调用,且 `Pred` 是透明的。这实现了异构查找,避免了实例化 `Key` 类型对象的开销。 + +在存在并发插入操作时,返回的值可能无法准确反映执行后表的真实状态。 + +--- + +==== 包含 +```c++ bool contains(const key_type& k) const; template bool contains(const K& k) const; ``` + +[horizontal] +返回值:;; 布尔值,表示表中是否存在键与`k`相等的元素。 +备注:;; `template` 重载仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为合法成员类型别名时才参与重载决议。标准库假定 `Hash` 可同时接受 `K` 与 `Key` 类型调用,且 `Pred` 是透明的。这实现了异构查找,避免了实例化 `Key` 类型对象的开销。 +在存在并发插入操作时,返回的值可能无法准确反映执行后表的真实状态。 + +--- +=== 桶接口 + +==== bucket_count +```c++ size_type bucket_count() const noexcept; ``` + +[horizontal] +返回值:;; 哈希桶数组的大小。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ float load_factor() const noexcept; ``` + +[horizontal] +返回值:;; `static_cast(size()) / static_cast(bucket_count())`;若`bucket_count() == 0`,则返回`0`。 + +--- + +==== max_load_factor + +```c++ float max_load_factor() const noexcept; ``` + +[horizontal] +返回值:;; 返回哈希表的最大负载因子。 + +--- + +==== 设置最大负载因子 +```c++ void max_load_factor(float z); ``` + +[horizontal] +效果:;; 不执行任何操作,因为不允许用户修改此参数。保留该函数是为了与 `boost::unordered_set` 保持兼容。 + +--- + + +==== max_load(最大负载) + +```c++ size_type max_load() const noexcept; ``` + +[horizontal] +返回值:;; 哈希表在不进行重哈希的前提下可容纳的最大元素数量(假设不会再删除任何元素)。 +备注:;; 构造完成、重哈希或清空后,哈希表的最大负载至少为 `max_load_factor() * bucket_count()`。在高负载条件下执行删除操作后,该数值可能会降低。 +在存在并发插入操作时,返回的值可能无法准确反映执行后哈希表的真实状态。 + +--- + +==== 重哈希 +```c++ void rehash(size_type n); ``` + +效果:;; 必要时调整哈希桶数组的大小,使桶数量至少为 `n`,且负载因子小于等于最大负载因子。 +适用情况下,该操作会**增大或缩小**哈希表的桶数量(`bucket_count()`)。 + +当 `size() == 0` 时,`rehash(0)` 会释放底层的哈希桶数组内存。 + +会使指向元素的指针和引用失效,并改变元素的存储顺序。 + +[horizontal] +抛出:;; 若抛出异常,函数无任何效果(哈希函数或比较函数抛出的异常除外)。 +并发:;; 阻塞`*this`。 +--- + +==== 保留 +```c++ void reserve(size_type n); ``` + +等价于 `a.rehash(ceil(n / a.max_load_factor()))`。 + +与 `rehash` 功能类似,该函数可用于**增加或减少**哈希表中的桶数量。 + +会使指向元素的指针和引用失效,并改变元素的存储顺序。 + +[horizontal] +抛出:;; 若抛出异常,函数不会产生任何效果(哈希表的哈希函数或比较函数抛出的异常除外)。 +并发:;; 阻塞`*this`。 + +--- + +=== 统计信息 + +==== get_stats +```c++ stats get_stats() const; ``` + +[horizontal] +返回值:;; 该哈希表迄今为止执行的插入与查找操作的统计信息。 +备注:;; 仅当 xref:reference/stats.adoc#stats[统计计算] 被 xref:concurrent_flat_set_boost_unordered_enable_stats[启用] 时,本接口方可使用。 + +--- + +==== reset_stats +```c++ void reset_stats() noexcept; ``` + +[horizontal] +效果:;; 将哈希表内部统计数据置零。 +备注:;; 仅当 xref:reference/stats.adoc#stats[统计计算] 被 xref:concurrent_flat_set_boost_unordered_enable_stats[启用] 时,本接口方可使用。 + +--- + +=== 推导指引 +如果以下任何一条件为真,则推导指引将不参与重载决议: + +- 该推导指引包含 `InputIterator` 模板参数,且为此参数推导出的类型不符合输入迭代器的要求。 - 该推导指引包含 `Allocator` 模板参数,且为该参数推导出的类型不符合分配器要求。 - 该推导指引包含 `Hash` 模板参数,且为该参数推导出的类型为整型或符合分配器要求。 - 该推导指引包含 `Pred` 模板参数,且为该参数推导出的类型符合分配器要求。 + +推导指引中的 `size++_++type` 参数类型,指向由该推导指引所推导容器类型的 `size++_++type` 成员类型。其默认值与所选构造函数的默认值一致。 + +==== _iter-value-type_ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-value-type__ = + typename std::iterator_traits::value_type; // exposition only +----- + +=== 相等性比较 + +==== operator +```c++ template bool operator==(const concurrent_flat_set& x, const concurrent_flat_set& y); ``` + +若 x.size() == y.size(),且对于 x 中的每个元素,y 中均存在一个具有相同键、值相等(使用 operator== 比较值类型)的元素,则返回 true。 + +[horizontal] +并发:;; 对`x`和`y`进行阻塞。 +备注:;; 若两个哈希表的相等判断谓词不一致,行为未定义。 + +--- + +==== operator! +```c++ template bool operator!=(const concurrent_flat_set& x, const concurrent_flat_set& y); ``` + +若 x.size() == y.size(),且对于 x 中的每个元素,y 中均存在一个具有相同键、值相等(使用 operator== 比较值类型)的元素,则返回 false。 + +[horizontal] +并发:;; 对`x`和`y`进行阻塞。 +备注:;; 若两个哈希表的相等判断谓词不一致,行为未定义。 + +--- + +=== 交换 +```c++ template void swap(concurrent_flat_set& x, concurrent_flat_set& y) noexcept(noexcept(x.swap(y))); ``` + +等效于 [listing,subs="+macros,+quotes"] +----- +x.xref:#concurrent_flat_set_swap[swap](y); +----- + +--- + +=== erase++_++if +```c++ template typename concurrent_flat_set::size_type erase_if(concurrent_flat_set& c, Predicate pred); ``` + +等效于 [listing,subs="+macros,+quotes"] +----- +c.xref:#concurrent_flat_set_erase_if[erase_if](pred); +----- + +=== 序列化 + +可通过本库提供的 API ,借助 link:../../../../../serialization/index.html[Boost.Serialization] 实现档归档/检索 `concurrent++_++flat++_++set` 。同时支持常规格式与 XML 格式的归档文件。 + +==== 将 concurrent++_++flat++_++set 保存到归档 + +将 `concurrent++_++flat++_++set` 容器 `x` 的所有元素保存到归档(XML 归档) `ar` 中。 + +[horizontal] +要求;; `value++_++type` 必须可序列化(支持 XML 序列化),且需要支持 Boost.Serialization 的 `save++_++construct++_++data` / `load++_++construct++_++data` 协议(该协议自动支持满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求的类型)。 并发性;; 阻塞于 `x` 。 + +--- + +==== 从归档加载 concurrent++_++flat++_++set + +删除 `concurrent++_++flat++_++set` 容器 `x` 中的所有现有元素,并从归档 `ar` (XML格式归档)中读取原始 `concurrent++_++flat++_++set` 容器 `other` 保存的元素副本并插入到 `x` 。 + +[horizontal] +要求;; `x.key++_++equal()` 需要在功能上等价于 `other.key++_++equal()` 。 并发性;; 阻塞于 `x` 。 diff --git a/doc/modules/ROOT/pages/reference/concurrent_node_map_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/concurrent_node_map_zh_Hans.adoc new file mode 100644 index 0000000..004cfde --- /dev/null +++ b/doc/modules/ROOT/pages/reference/concurrent_node_map_zh_Hans.adoc @@ -0,0 +1,1506 @@ +[#concurrent_node_map] +== 类模板 concurrent++_++node++_++map + +:idprefix: concurrent_node_map_ + +`boost::concurrent++_++node++_++map` —— 一种基于节点的哈希表,用于建立唯一键与对应值的关联,并支持在无外部同步机制的情况下并发执行元素插入、擦除、查找和访问操作。 + +尽管 `boost::concurrent++_++node++_++map` 的行为类似于容器,但它并不符合标准 C{plus}{plus} 的 https://en.cppreference.com/w/cpp/named_req/Container[容器] 概念模型。具体而言,它不提供迭代器及相关操作(如 `begin` 、 `end` 等)。元素的访问和修改通过用户提供的__访问函数__来实现,这些函数被传递至 `concurrent++_++node++_++map` 的操作中,并在其内部以受控方式执行。这种基于访问的 API 设计能够支持低争用的并发应用场景。 + +`boost::concurrent++_++node++_++map` 的内部数据结构与 `boost::unordered++_++node++_++map` 类似。与 `boost::concurrent++_++flat++_++map` 不同,它提供了指针稳定性和节点处理功能,但代价是可能带来性能上的损失。 + +=== 概要 + +[listing,subs="+macros,+quotes"] +----- +// #include xref:reference/header_concurrent_node_map.adoc[``] + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + class concurrent_node_map { + public: + // types + using key_type = Key; + using mapped_type = T; + using value_type = std::pair; + using init_type = std::pair< + typename std::remove_const::type, + typename std::remove_const::type + >; + using hasher = Hash; + using key_equal = Pred; + using allocator_type = Allocator; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + using node_type = _implementation-defined_; + using insert_return_type = _implementation-defined_; + + using stats = xref:reference/stats.adoc#stats_stats_type[__stats-type__]; // if statistics are xref:concurrent_node_map_boost_unordered_enable_stats[enabled] + + // constants + static constexpr size_type xref:#concurrent_node_map_constants[bulk_visit_size] = _implementation-defined_; + + // construct/copy/destroy + xref:#concurrent_node_map_default_constructor[concurrent_node_map](); + explicit xref:#concurrent_node_map_bucket_count_constructor[concurrent_node_map](size_type n, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + template + xref:#concurrent_node_map_iterator_range_constructor[concurrent_node_map](InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#concurrent_node_map_copy_constructor[concurrent_node_map](const concurrent_node_map& other); + xref:#concurrent_node_map_move_constructor[concurrent_node_map](concurrent_node_map&& other); + template + xref:#concurrent_node_map_iterator_range_constructor_with_allocator[concurrent_node_map](InputIterator f, InputIterator l,const allocator_type& a); + explicit xref:#concurrent_node_map_allocator_constructor[concurrent_node_map](const Allocator& a); + xref:#concurrent_node_map_copy_constructor_with_allocator[concurrent_node_map](const concurrent_node_map& other, const Allocator& a); + xref:#concurrent_node_map_move_constructor_with_allocator[concurrent_node_map](concurrent_node_map&& other, const Allocator& a); + xref:#concurrent_node_map_move_constructor_from_unordered_node_map[concurrent_node_map](unordered_node_map&& other); + xref:#concurrent_node_map_initializer_list_constructor[concurrent_node_map](std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#concurrent_node_map_bucket_count_constructor_with_allocator[concurrent_node_map](size_type n, const allocator_type& a); + xref:#concurrent_node_map_bucket_count_constructor_with_hasher_and_allocator[concurrent_node_map](size_type n, const hasher& hf, const allocator_type& a); + template + xref:#concurrent_node_map_iterator_range_constructor_with_bucket_count_and_allocator[concurrent_node_map](InputIterator f, InputIterator l, size_type n, + const allocator_type& a); + template + xref:#concurrent_node_map_iterator_range_constructor_with_bucket_count_and_hasher[concurrent_node_map](InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); + xref:#concurrent_node_map_initializer_list_constructor_with_allocator[concurrent_node_map](std::initializer_list il, const allocator_type& a); + xref:#concurrent_node_map_initializer_list_constructor_with_bucket_count_and_allocator[concurrent_node_map](std::initializer_list il, size_type n, + const allocator_type& a); + xref:#concurrent_node_map_initializer_list_constructor_with_bucket_count_and_hasher_and_allocator[concurrent_node_map](std::initializer_list il, size_type n, const hasher& hf, + const allocator_type& a); + xref:#concurrent_node_map_destructor[~concurrent_node_map](); + concurrent_node_map& xref:#concurrent_node_map_copy_assignment[operator++=++](const concurrent_node_map& other); + concurrent_node_map& xref:#concurrent_node_map_move_assignment[operator++=++](concurrent_node_map&& other) ++noexcept( + (boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_move_assignment::value) && + std::is_same::value);++ + concurrent_node_map& xref:#concurrent_node_map_initializer_list_assignment[operator++=++](std::initializer_list); + allocator_type xref:#concurrent_node_map_get_allocator[get_allocator]() const noexcept; + + + // visitation + template size_t xref:#concurrent_node_map_cvisit[visit](const key_type& k, F f); + template size_t xref:#concurrent_node_map_cvisit[visit](const key_type& k, F f) const; + template size_t xref:#concurrent_node_map_cvisit[cvisit](const key_type& k, F f) const; + template size_t xref:#concurrent_node_map_cvisit[visit](const K& k, F f); + template size_t xref:#concurrent_node_map_cvisit[visit](const K& k, F f) const; + template size_t xref:#concurrent_node_map_cvisit[cvisit](const K& k, F f) const; + + template + size_t xref:concurrent_node_map_bulk_visit[visit](FwdIterator first, FwdIterator last, F f); + template + size_t xref:concurrent_node_map_bulk_visit[visit](FwdIterator first, FwdIterator last, F f) const; + template + size_t xref:concurrent_node_map_bulk_visit[cvisit](FwdIterator first, FwdIterator last, F f) const; + + template size_t xref:#concurrent_node_map_cvisit_all[visit_all](F f); + template size_t xref:#concurrent_node_map_cvisit_all[visit_all](F f) const; + template size_t xref:#concurrent_node_map_cvisit_all[cvisit_all](F f) const; + template + void xref:#concurrent_node_map_parallel_cvisit_all[visit_all](ExecutionPolicy&& policy, F f); + template + void xref:#concurrent_node_map_parallel_cvisit_all[visit_all](ExecutionPolicy&& policy, F f) const; + template + void xref:#concurrent_node_map_parallel_cvisit_all[cvisit_all](ExecutionPolicy&& policy, F f) const; + + template bool xref:#concurrent_node_map_cvisit_while[visit_while](F f); + template bool xref:#concurrent_node_map_cvisit_while[visit_while](F f) const; + template bool xref:#concurrent_node_map_cvisit_while[cvisit_while](F f) const; + template + bool xref:#concurrent_node_map_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f); + template + bool xref:#concurrent_node_map_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f) const; + template + bool xref:#concurrent_node_map_parallel_cvisit_while[cvisit_while](ExecutionPolicy&& policy, F f) const; + + // capacity + ++[[nodiscard]]++ bool xref:#concurrent_node_map_empty[empty]() const noexcept; + size_type xref:#concurrent_node_map_size[size]() const noexcept; + size_type xref:#concurrent_node_map_max_size[max_size]() const noexcept; + + // modifiers + template bool xref:#concurrent_node_map_emplace[emplace](Args&&... args); + bool xref:#concurrent_node_map_copy_insert[insert](const value_type& obj); + bool xref:#concurrent_node_map_copy_insert[insert](const init_type& obj); + bool xref:#concurrent_node_map_move_insert[insert](value_type&& obj); + bool xref:#concurrent_node_map_move_insert[insert](init_type&& obj); + template size_type xref:#concurrent_node_map_insert_iterator_range[insert](InputIterator first, InputIterator last); + size_type xref:#concurrent_node_map_insert_initializer_list[insert](std::initializer_list il); + insert_return_type xref:#concurrent_node_map_insert_node[insert](node_type&& nh); + + template bool xref:#concurrent_node_map_emplace_or_cvisit[emplace_or_visit](Args&&... args, F&& f); + template bool xref:#concurrent_node_map_emplace_or_cvisit[emplace_or_cvisit](Args&&... args, F&& f); + template bool xref:#concurrent_node_map_copy_insert_or_cvisit[insert_or_visit](const value_type& obj, F f); + template bool xref:#concurrent_node_map_copy_insert_or_cvisit[insert_or_cvisit](const value_type& obj, F f); + template bool xref:#concurrent_node_map_copy_insert_or_cvisit[insert_or_visit](const init_type& obj, F f); + template bool xref:#concurrent_node_map_copy_insert_or_cvisit[insert_or_cvisit](const init_type& obj, F f); + template bool xref:#concurrent_node_map_move_insert_or_cvisit[insert_or_visit](value_type&& obj, F f); + template bool xref:#concurrent_node_map_move_insert_or_cvisit[insert_or_cvisit](value_type&& obj, F f); + template bool xref:#concurrent_node_map_move_insert_or_cvisit[insert_or_visit](init_type&& obj, F f); + template bool xref:#concurrent_node_map_move_insert_or_cvisit[insert_or_cvisit](init_type&& obj, F f); + template + size_type xref:#concurrent_node_map_insert_iterator_range_or_visit[insert_or_visit](InputIterator first, InputIterator last, F f); + template + size_type xref:#concurrent_node_map_insert_iterator_range_or_visit[insert_or_cvisit](InputIterator first, InputIterator last, F f); + template size_type xref:#concurrent_node_map_insert_initializer_list_or_visit[insert_or_visit](std::initializer_list il, F f); + template size_type xref:#concurrent_node_map_insert_initializer_list_or_visit[insert_or_cvisit](std::initializer_list il, F f); + template insert_return_type xref:#concurrent_node_map_insert_node_or_visit[insert_or_visit](node_type&& nh, F f); + template insert_return_type xref:#concurrent_node_map_insert_node_or_visit[insert_or_cvisit](node_type&& nh, F f); + + template + bool xref:#concurrent_node_map_emplace_and_cvisit[emplace_and_visit](Args&&... args, F1&& f1, F2&& f2); + template + bool xref:#concurrent_node_map_emplace_and_cvisit[emplace_and_cvisit](Args&&... args, F1&& f1, F2&& f2); + template bool xref:#concurrent_node_map_copy_insert_and_cvisit[insert_and_visit](const value_type& obj, F1 f1, F2 f2); + template bool xref:#concurrent_node_map_copy_insert_and_cvisit[insert_and_cvisit](const value_type& obj, F1 f1, F2 f2); + template bool xref:#concurrent_node_map_copy_insert_and_cvisit[insert_and_visit](const init_type& obj, F1 f1, F2 f2); + template bool xref:#concurrent_node_map_copy_insert_and_cvisit[insert_and_cvisit](const init_type& obj, F1 f1, F2 f2); + template bool xref:#concurrent_node_map_move_insert_and_cvisit[insert_and_visit](value_type&& obj, F1 f1, F2 f2); + template bool xref:#concurrent_node_map_move_insert_and_cvisit[insert_and_cvisit](value_type&& obj, F1 f1, F2 f2); + template bool xref:#concurrent_node_map_move_insert_and_cvisit[insert_and_visit](init_type&& obj, F1 f1, F2 f2); + template bool xref:#concurrent_node_map_move_insert_and_cvisit[insert_and_cvisit](init_type&& obj, F1 f1, F2 f2); + template + size_type xref:#concurrent_node_map_insert_iterator_range_and_visit[insert_and_visit](InputIterator first, InputIterator last, F1 f1, F2 f2); + template + size_type xref:#concurrent_node_map_insert_iterator_range_and_visit[insert_and_cvisit](InputIterator first, InputIterator last, F1 f1, F2 f2); + template + size_type xref:#concurrent_node_map_insert_initializer_list_and_visit[insert_and_visit](std::initializer_list il, F1 f1, F2 f2); + template + size_type xref:#concurrent_node_map_insert_initializer_list_and_visit[insert_and_cvisit](std::initializer_list il, F1 f1, F2 f2); + template + insert_return_type xref:#concurrent_node_map_insert_node_and_visit[insert_and_visit](node_type&& nh, F1 f1, F2 f2); + template + insert_return_type xref:#concurrent_node_map_insert_node_and_visit[insert_and_cvisit](node_type&& nh, F1 f1, F2 f2); + + template bool xref:#concurrent_node_map_try_emplace[try_emplace](const key_type& k, Args&&... args); + template bool xref:#concurrent_node_map_try_emplace[try_emplace](key_type&& k, Args&&... args); + template bool xref:#concurrent_node_map_try_emplace[try_emplace](K&& k, Args&&... args); + + template + bool xref:#concurrent_node_map_try_emplace_or_cvisit[try_emplace_or_visit](const key_type& k, Args&&... args, F&& f); + template + bool xref:#concurrent_node_map_try_emplace_or_cvisit[try_emplace_or_cvisit](const key_type& k, Args&&... args, F&& f); + template + bool xref:#concurrent_node_map_try_emplace_or_cvisit[try_emplace_or_visit](key_type&& k, Args&&... args, F&& f); + template + bool xref:#concurrent_node_map_try_emplace_or_cvisit[try_emplace_or_cvisit](key_type&& k, Args&&... args, F&& f); + template + bool xref:#concurrent_node_map_try_emplace_or_cvisit[try_emplace_or_visit](K&& k, Args&&... args, F&& f); + template + bool xref:#concurrent_node_map_try_emplace_or_cvisit[try_emplace_or_cvisit](K&& k, Args&&... args, F&& f); + + template + bool xref:#concurrent_node_map_try_emplace_and_cvisit[try_emplace_and_visit](const key_type& k, Args&&... args, F1&& f1, F2&& f2); + template + bool xref:#concurrent_node_map_try_emplace_and_cvisit[try_emplace_and_cvisit](const key_type& k, Args&&... args, F1&& f1, F2&& f2); + template + bool xref:#concurrent_node_map_try_emplace_and_cvisit[try_emplace_and_visit](key_type&& k, Args&&... args, F1&& f1, F2&& f2); + template + bool xref:#concurrent_node_map_try_emplace_and_cvisit[try_emplace_and_cvisit](key_type&& k, Args&&... args, F1&& f1, F2&& f2); + template + bool xref:#concurrent_node_map_try_emplace_and_cvisit[try_emplace_and_visit](K&& k, Args&&... args, F1&& f1, F2&& f2); + template + bool xref:#concurrent_node_map_try_emplace_and_cvisit[try_emplace_and_cvisit](K&& k, Args&&... args, F1&& f1, F2&& f2); + + + template bool xref:#concurrent_node_map_insert_or_assign[insert_or_assign](const key_type& k, M&& obj); + template bool xref:#concurrent_node_map_insert_or_assign[insert_or_assign](key_type&& k, M&& obj); + template bool xref:#concurrent_node_map_insert_or_assign[insert_or_assign](K&& k, M&& obj); + + size_type xref:#concurrent_node_map_erase[erase](const key_type& k); + template size_type xref:#concurrent_node_map_erase[erase](const K& k); + + template size_type xref:#concurrent_node_map_erase_if_by_key[erase_if](const key_type& k, F f); + template size_type xref:#concurrent_node_map_erase_if_by_key[erase_if](const K& k, F f); + template size_type xref:#concurrent_node_map_erase_if[erase_if](F f); + template void xref:#concurrent_node_map_parallel_erase_if[erase_if](ExecutionPolicy&& policy, F f); + + void xref:#concurrent_node_map_swap[swap](concurrent_node_map& other) + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_swap::value); + + node_type xref:#concurrent_node_map_extract[extract](const key_type& k); + template node_type xref:#concurrent_node_map_extract[extract](const K& k); + + template node_type xref:#concurrent_node_map_extract_if[extract_if](const key_type& k, F f); + template node_type xref:#concurrent_node_map_extract[extract_if](const K& k, F f); + + void xref:#concurrent_node_map_clear[clear]() noexcept; + + template + size_type xref:#concurrent_node_map_merge[merge](concurrent_node_map& source); + template + size_type xref:#concurrent_node_map_merge[merge](concurrent_node_map&& source); + + // observers + hasher xref:#concurrent_node_map_hash_function[hash_function]() const; + key_equal xref:#concurrent_node_map_key_eq[key_eq]() const; + + // map operations + size_type xref:#concurrent_node_map_count[count](const key_type& k) const; + template + size_type xref:#concurrent_node_map_count[count](const K& k) const; + bool xref:#concurrent_node_map_contains[contains](const key_type& k) const; + template + bool xref:#concurrent_node_map_contains[contains](const K& k) const; + + // bucket interface + size_type xref:#concurrent_node_map_bucket_count[bucket_count]() const noexcept; + + // hash policy + float xref:#concurrent_node_map_load_factor[load_factor]() const noexcept; + float xref:#concurrent_node_map_max_load_factor[max_load_factor]() const noexcept; + void xref:#concurrent_node_map_set_max_load_factor[max_load_factor](float z); + size_type xref:#concurrent_node_map_max_load[max_load]() const noexcept; + void xref:#concurrent_node_map_rehash[rehash](size_type n); + void xref:#concurrent_node_map_reserve[reserve](size_type n); + + // statistics (if xref:concurrent_node_map_boost_unordered_enable_stats[enabled]) + stats xref:#concurrent_node_map_get_stats[get_stats]() const; + void xref:#concurrent_node_map_reset_stats[reset_stats]() noexcept; + }; + + // Deduction Guides + template>, + class Pred = std::equal_to>, + class Allocator = std::allocator>> + concurrent_node_map(InputIterator, InputIterator, typename xref:#concurrent_node_map_deduction_guides[__see below__]::size_type = xref:#concurrent_node_map_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> concurrent_node_map, xref:#concurrent_node_map_iter_mapped_type[__iter-mapped-type__], Hash, + Pred, Allocator>; + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + concurrent_node_map(std::initializer_list>, + typename xref:#concurrent_node_map_deduction_guides[__see below__]::size_type = xref:#concurrent_node_map_deduction_guides[__see below__], Hash = Hash(), + Pred = Pred(), Allocator = Allocator()) + -> concurrent_node_map; + + template + concurrent_node_map(InputIterator, InputIterator, typename xref:#concurrent_node_map_deduction_guides[__see below__]::size_type, Allocator) + -> concurrent_node_map, xref:#concurrent_node_map_iter_mapped_type[__iter-mapped-type__], + boost::hash>, + std::equal_to>, Allocator>; + + template + concurrent_node_map(InputIterator, InputIterator, Allocator) + -> concurrent_node_map, xref:#concurrent_node_map_iter_mapped_type[__iter-mapped-type__], + boost::hash>, + std::equal_to>, Allocator>; + + template + concurrent_node_map(InputIterator, InputIterator, typename xref:#concurrent_node_map_deduction_guides[__see below__]::size_type, Hash, + Allocator) + -> concurrent_node_map, xref:#concurrent_node_map_iter_mapped_type[__iter-mapped-type__], Hash, + std::equal_to>, Allocator>; + + template + concurrent_node_map(std::initializer_list>, typename xref:#concurrent_node_map_deduction_guides[__see below__]::size_type, + Allocator) + -> concurrent_node_map, std::equal_to, Allocator>; + + template + concurrent_node_map(std::initializer_list>, Allocator) + -> concurrent_node_map, std::equal_to, Allocator>; + + template + concurrent_node_map(std::initializer_list>, typename xref:#concurrent_node_map_deduction_guides[__see below__]::size_type, + Hash, Allocator) + -> concurrent_node_map, Allocator>; + +} // namespace unordered +} // namespace boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_Key_ +.2+|`std::pair` must be https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^] +`std::pair` 对象 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] 到容器中,并且必须支持从容器中 https://en.cppreference.com/w/cpp/named_req/Erasable[擦除] 。 + +|_T_ + +|_Hash_ +|A unary function object type that acts a hash function for a `Key`. It takes a single argument of type `Key` and returns a value of type `std::size_t`. + +|_Pred_ +|A binary function object that induces an equivalence relation on values of type `Key`. It takes two arguments of type `Key` and returns a value of type `bool`. + +|_Allocator_ +|An allocator whose value type is the same as the table's value type. +支持使用 https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[花式指针] 的分配器。 + +|=== + +哈希表的元素节点存储在内部**桶数组**中。节点会根据其元素的哈希值插入到对应的桶中;若该桶已被占用(即发生**哈希冲突**),则使用原位置附近的可用桶。 + +通过调用 `insert`/`emplace`,或执行 `rehash`/`reserve` 操作,可自动增大桶数组的大小。哈希表的**负载因子**(元素数量除以桶数量)永远不会大于 `max_load_factor()`,仅在尺寸极小时,实现可能允许更高的负载。 + +若`link:../../../../../container_hash/doc/html/hash.html#ref_hash_is_avalanchinghash[hash_is_avalanching]::value` 为 `true`,则直接使用原哈希函数;否则会添加一个比特混合后处理阶段,以额外计算开销为代价提升哈希质量。 + +--- + +=== 并发要求与保证 + +要求对同一 `Hash` 或 `Pred` 常量实例并发调用 `operator()` 时不得引入数据竞争。对于 `Alloc` (即 `Allocator` 或其重绑定后的任意分配器类型),在同一实例 `al` 上并发调用以下操作时不得引入数据竞争: + +* 从 `al` 复制构造重新绑定的分配器 +* `std::allocator_traits::allocate` +* `std::allocator_traits::deallocate` +* `std::allocator_traits::construct` +* `std::allocator_traits::destroy` + +通常而言,若 `Hash` 、 `Pred` 和 `Allocator` 这些类型不包含状态,或其操作仅涉及对内部数据成员的常量访问,即可满足上述要求。 + +除析构操作外,对同一个 `concurrent++_++node++_++map` 实例并发调用任何操作都不会引入数据竞争——即这些操作是线程安全的。 + +如果某个操作 *op* 被显式指定为__阻塞于__ `x` (其中 `x` 是 `boost::concurrent++_++node++_++map` 的实例),则先前对 `x` 的阻塞操作将与 *op* 同步。因此,在多线程场景中,对同一 `concurrent++_++node++_++map` 的阻塞操作将按顺序执行。 + +若某个操作仅在触发内部重哈希时才会阻塞于 _`x`_,则称该操作__阻塞于 _`x`_ 的重哈希过程__。 + +当由 `boost::concurrent++_++node++_++map` 在内部执行时,用户提供的访问函数对传递的元素执行以下操作不会引入数据竞争: + +* 对元素的读取访问。 +* 对元素的非可变修改。 +* 对元素的可变修改: +** 在容器接受两个访问函数的操作中,此条件始终适用于第一个访问函数。 ** 在名称不包含 `cvisit` 的非常量容器函数中,此条件适用于最后一个(或唯一一个)访问函数。 + +任何插入或修改元素 `e` 的 `boost::concurrent++_++node++_++map 操作 ` 都会与对 `e` 执行的内部访问函数的调用同步。 + +由 `boost::concurrent++_++node++_++map` 实例 `x` 执行的访问函数不允许调用 `x` 上的任何操作;仅当对另一 `boost::concurrent++_++node++_++map` 实例 `y` 的并发未完成操作不会直接或间接访问 `x` 时,才会允许调用 `y` 上的操作。 + +--- + +=== 配置宏 + +==== `BOOST_UNORDERED_DISABLE_REENTRANCY_CHECK` + +在调试版本中(更准确地说,当未定义 link:../../../../../assert/doc/html/assert.html#boost_assert_is_void[`BOOST++_++ASSERT++_++IS++_++VOID`] 时),系统会检测__容器重入__行为(即在访问 `m` 元素的函数内部非法调用 `m` 上的操作),并通过 `BOOST++_++ASSERT++_++MSG` 发出信号。若需关注运行时速度,可通过全局定义此宏来禁用该功能。 + +--- + +==== `BOOST_UNORDERED_ENABLE_STATS` + +全局定义此宏以启用容器的 xref:reference/stats.adoc#stats[统计计算] 功能。请注意,此选项会降低多数操作的总体性能。 + +--- + +=== 类型定义 + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ node_type; +---- + +用于保存提取的表元素的类,建模为 https://en.cppreference.com/w/cpp/container/node_handle[NodeHandle] 。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ insert_return_type; +---- + +内部类模板的特化: + +[source,c++,subs=+quotes] +---- +template +struct _insert_return_type_ // name is exposition only +{ + bool inserted; + NodeType node; +}; +---- + +其中 `NodeType` = `node++_++type` 。 + +--- + +=== 常量 + +```cpp static constexpr size_type bulk_visit_size; ``` + +内部用于 xref:concurrent_node_map_bulk_visit[批量访问] 操作的块大小。 + +--- + +=== 构造函数 + +==== 默认构造函数 +```c++ concurrent_node_map(); ``` + +构造一个空哈希表,使用 `hasher()` 作为哈希函数,`key_equal()` 作为键相等谓词,`allocator_type()` 作为分配器。 + +[horizontal] +后置条件:;; `size() == 0` +要求:;; 若使用默认参数,则 `hasher`、`key_equal` 和 `allocator_type` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[默认可构造]。 + +--- + +==== 桶数构造函数 +```c++ explicit concurrent_node_map(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); ``` + +构造一个至少包含 `n` 个桶的空哈希表,使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器。 + +[horizontal] +后置条件:;; `size() == 0` +要求:;; 若使用默认参数,则 `hasher`、`key_equal` 和 `allocator_type` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[默认可构造]。 + +--- + +==== 迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + concurrent_node_map(InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `eql` 作为键相等性谓词、 `a` 作为分配器,并将 `++[++f, l)` 范围内的元素插入其中。 + +[horizontal] +要求;; 若使用默认值,则 `hasher` 、 `key++_++equal` 和 `allocator++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 复制构造函数 +```c++ concurrent_node_map(concurrent_node_map const& other); ``` + +**复制构造函数**。复制容器内元素、哈希函数、谓词及分配器。 + +若 `Allocator::select_on_container_copy_construction` 存在且签名正确,则分配器将由其返回值构造。 + +[horizontal] +要求:;; `value_type` 是可复制构造的 +并发:;; 阻塞 `other` + +--- + +==== 移动构造函数 +```c++ concurrent_node_map(concurrent_node_map&& other); ``` + +移动构造函数。将 `other` 的内部桶数组直接转移到新哈希表中。哈希函数、谓词和分配器均从 `other` 移动构造而来。若启用了 xref:concurrent_node_map_boost_unordered_enable_stats[统计功能],则从 `other` 转移内部统计信息,并调用 `other.reset_stats()`。 + +[horizontal] +并发:;; 阻塞 `other`。 + +--- + +==== 带分配器的迭代器范围构造函数 +```c++ template concurrent_node_map(InputIterator f, InputIterator l, const allocator_type& a); ``` + +构造一个以 `a` 为分配器、使用默认哈希函数与键相等谓词的空哈希表,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:;; `hasher`、`key_equal` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[默认可构造]。 + +--- + +==== 分配器构造函数 +```c++ explicit concurrent_node_map(Allocator const& a); ``` + +构造一个空哈希表,使用分配器 `a`。 + +--- + +==== 带分配器的复制构造函数 +```c++ concurrent_node_map(concurrent_node_map const& other, Allocator const& a); ``` + +构造一个哈希表,复制 `other` 的容器元素、哈希函数和谓词,但使用分配器 `a`。 + +[horizontal] +并发:;; 阻塞 `other`。 + +--- + +==== 带分配器的移动构造函数 +```c++ concurrent_node_map(concurrent_node_map&& other, Allocator const& a); ``` + +若 `a == other.get_allocator()`,则将 `other` 的元素直接转移到新哈希表中;否则从 `other` 的元素移动构造出新元素。哈希函数与谓词从 `other` 移动构造而来,分配器从 `a` 复制构造而来。 +若启用了 xref:concurrent_node_map_boost_unordered_enable_stats[统计功能],仅当 `a == other.get_allocator()` 时从 `other` 转移内部统计信息,且始终调用 `other.reset_stats()`。 + +[horizontal] +并发:;; 阻塞 `other`。 + +--- + +==== 基于 unordered_node_map 的移动构造函数 + +```c++ concurrent_node_map(unordered_node_map&& other); ``` + +通过 xref:#unordered_node_map[`unordered_node_map`] 进行移动构造。将 `other` 的内部桶数组直接转移到新容器中。哈希函数、谓词和分配器均从 `other` 移动构造而来。若启用了 xref:concurrent_node_map_boost_unordered_enable_stats[统计功能],则从 `other` 转移内部统计信息,并调用 `other.reset_stats()`。 + +[horizontal] +复杂度:;; O(`bucket_count()`) + +--- + +==== 初始化列表构造函数 +[source,c++,subs="+quotes"] +---- +concurrent_node_map(std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `eql` 作为键相等性谓词、 `a` 作为分配器,并 `il` 中的元素插入其中。 + +[horizontal] +要求;; 若使用默认值,则 `hasher` 、 `key++_++equal` 和 `allocator++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带分配器的桶数构造函数 +```c++ concurrent_node_map(size_type n, allocator_type const& a); ``` + +构造一个至少包含 `n` 个桶的空哈希表,使用 `hf` 作为哈希函数,默认键相等谓词,并以 `a` 作为分配器。 + +[horizontal] +后置条件:;; `size() == 0` +要求:;; `hasher` 和 `key_equal` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[默认可构造]。 + +--- + +==== 带哈希函数和分配器的桶数构造函数 +```c++ concurrent_node_map(size_type n, hasher const& hf, allocator_type const& a); ``` + +构造一个至少包含 `n` 个桶的空哈希表,使用 `hf` 作为哈希函数,默认键相等谓词,并以 `a` 作为分配器。 + +[horizontal] +后置条件:;; `size() == 0` +要求:;; `key_equal` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[默认可构造]。 + +--- + +==== 带桶数和分配器的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + concurrent_node_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 作为分配器以及默认的哈希函数和键相等性谓词,并将 `++[++f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:;; `hasher`、`key_equal` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[默认可构造]。 + +--- + +==== 带桶数和哈希函数的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- + template + concurrent_node_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `a` 作为分配器以及默认的键相等性谓词,并将 `++[++f, l)` 范围内的元素插入其中。 + +[horizontal] +要求;; `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带分配器的初始化列表构造函数 + +```c++ concurrent_node_map(std::initializer_list il, const allocator_type& a); ``` + +构造一个使用分配器 `a`、默认哈希函数和默认键相等谓词的空哈希表,并将 `il` 中的元素插入其中。 + +[horizontal] +Requires:;; `hasher` and `key_equal` need to be https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]. + +--- + +==== 带桶数和分配器的初始化列表构造函数 + +```c++ concurrent_node_map(std::initializer_list il, size_type n, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空哈希表,使用分配器 `a`、默认哈希函数和默认键相等谓词,并将 `il` 中的元素插入其中。 + +[horizontal] +Requires:;; `hasher` and `key_equal` need to be https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]. + +--- + +==== 带桶数、哈希函数和分配器的初始化列表构造函数 + +```c++ concurrent_node_map(std::initializer_list il, size_type n, const hasher& hf, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空哈希表,使用 `hf` 作为哈希函数,`a` 作为分配器,默认键相等谓词,并将 `il` 中的元素插入其中。 + +[horizontal] +要求;; `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +=== 析构函数 + +```c++ ~concurrent_node_map(); ``` + +[horizontal] +注意:;; 析构函数会作用于所有元素,且所有内存都会被释放 + +--- + +=== 赋值操作 + +==== 复制赋值 + +```c++ concurrent_node_map& operator=(concurrent_node_map const& other); ``` + +赋值运算符。销毁已存在的所有元素,从 `other` 复制赋值哈希函数和谓词;若 `Alloc::propagate_on_container_copy_assignment` 存在且其值为 `true`,则从 `other` 复制赋值分配器;最后插入 `other` 元素的副本。 + +[horizontal] +要求:;; `value_type` 必须满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入] +并发:;; 阻塞 `*this` 与 `other` + +--- + +==== 移动赋值 +```c++ concurrent_node_map& operator=(concurrent_node_map&& other) noexcept((boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_move_assignment::value) && std::is_same::value); ```移动赋值运算符。销毁已存在的所有元素,与 `other` 交换哈希函数和谓词;若 `Alloc::propagate_on_container_move_assignment` 存在且其值为 `true`,则从 `other` 移动赋值分配器。 +若此时分配器与 `other.get_allocator()` 相等,则将 `other` 的内部桶数组直接转移给 `*this`;否则插入 `other` 元素的移动构造副本。若启用了 xref:concurrent_node_map_boost_unordered_enable_stats[统计功能],仅当最终分配器与 `other.get_allocator()` 相等时,从 `other` 转移内部统计信息,且始终调用 `other.reset_stats()`。 + +[horizontal] +并发:;; 阻塞 `*this` 与 `other` + +--- + +==== 初始化列表赋值 +```c++ concurrent_node_map& operator=(std::initializer_list il); ``` + +通过初始化列表赋值。销毁所有已存在的元素。 + +[horizontal] +要求:;; `value_type` 必须满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入] +并发:;; 阻塞 `*this` + +--- + +=== 访问操作 + +==== [c]visit + +```c++ template size_t visit(const key_type& k, F f); template size_t visit(const key_type& k, F f) const; template size_t cvisit(const key_type& k, F f) const; template size_t visit(const K& k, F f); template size_t visit(const K& k, F f) const; template size_t cvisit(const K& k, F f) const; ``` + +若存在键与 `k` 等价的元素 `x`,则以 `x` 的引用调用函数 `f`。当且仅当 `*this` 为常量时,该引用为常量引用。 + +[horizontal] +返回值:;; 访问的元素数量(0 或 1)。 +注意:;; 仅当 `Hash::is_transparent` 与 `Pred::is_transparent` 为合法成员别名时,`template` 重载版本才会参与重载决议。库假定 `Hash` 可同时以 `K` 类型与 `Key` 类型调用,且 `Pred` 是透明的。这支持异构查找,避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 批量访问 + +```c++ template size_t visit(FwdIterator first, FwdIterator last, F f); template size_t visit(FwdIterator first, FwdIterator last, F f) const; template size_t cvisit(FwdIterator first, FwdIterator last, F f) const; ``` + +对范围 [`first`, `last`) 中的每个元素 `k`,若容器内存在键与 `k` 等价的元素 `x`,则以 `x` 的引用调用函数 `f`。当且仅当 `*this` 为常量时,该引用为常量引用。 + +尽管功能上等同于对每个键单独调用 `[c]visit`,但得益于内部的流式优化,批量访问通常性能更高。建议当 `std::distance(first,last)` 至少达到 `bulk_visit_size` 时使用批量访问以获得性能提升;超过该大小后,性能不会进一步提升。 + +[horizontal] +要求:;; `FwdIterator` 是前向迭代器(C++11 至 C++17),或满足 C++20 及之后标准的 `std::forward_iterator` 概念。 +对于 `K = std::iterator_traits::value_type`,要么 `K` 是 `key_type`,要么 `Hash::is_transparent` 和 `Pred::is_transparent` 是合法的成员别名。 +在后一种情况下,库假定 `Hash` 可同时接收 `K` 与 `Key` 类型调用,且 `Pred` 是透明的。这支持异构查找,避免了实例化 `Key` 类型对象的开销。 +返回值:;; 被访问的元素总数。 + +--- + +==== [c]visit_all + +```c++ template size_t visit_all(F f); template size_t visit_all(F f) const; template size_t cvisit_all(F f) const; ``` + +依次以表中每个元素的引用调用函数 `f`。当且仅当 `*this` 为常量时,该引用为常量引用。 + +[horizontal] +返回值:;; 被访问的元素数量。 + +--- + +==== 并行 ++[++c++]++visit++_++all + +```c++ template void visit_all(ExecutionPolicy&& policy, F f); template void visit_all(ExecutionPolicy&& policy, F f) const; template void cvisit_all(ExecutionPolicy&& policy, F f) const; ``` + +以表中每个元素的引用调用函数 `f`。当且仅当 `*this` 为常量时,该引用为常量引用。执行过程将根据指定执行策略的语义进行并行化处理。 + +[horizontal] +抛出异常:;; 依据所使用执行策略的异常处理机制,若 `f` 内部抛出异常,则可能调用 `std::terminate`。 +注意:;; 仅在支持 C++17 并行算法的编译器中可用。 +仅当 `std::is_execution_policy_v>` 为 `true` 时,这些重载版本才参与重载决议。 +不允许使用无序执行策略。 + +--- + +==== [c]visit_while + +```c++ template bool visit_while(F f); template bool visit_while(F f) const; template bool cvisit_while(F f) const; ``` + +依次以哈希表中每个元素的引用调用函数 `f`,直到 `f` 返回 `false` 或遍历完所有元素。当且仅当 `*this` 为常量时,元素的引用为常量引用。 + +[horizontal] +返回值:;; 当且仅当 `f` 曾返回 `false` 时,返回 `false`。 + +--- + +==== 并行 ++[++c++]++visit++_++while + +```c++ template bool visit_while(ExecutionPolicy&& policy, F f); template bool visit_while(ExecutionPolicy&& policy, F f) const; template bool cvisit_while(ExecutionPolicy&& policy, F f) const; ``` + +以哈希表中每个元素的引用调用函数 `f`,直到 `f` 返回 `false` 或遍历完所有元素。当且仅当 `*this` 为常量时,元素的引用为常量引用。执行过程将根据指定执行策略的语义进行并行化处理。 + +[horizontal] +返回值:;; 当且仅当 `f` 曾返回 `false` 时,返回 `false`。 +抛出异常:;; 依据所使用执行策略的异常处理机制,若 `f` 内部抛出异常,则可能调用 `std::terminate`。 +注意:;; 仅在支持 C++17 并行算法的编译器中可用。 +仅当 `std::is_execution_policy_v>` 为 `true` 时,这些重载版本才参与重载决议。 +不允许使用无序执行策略。 +并行化意味着:一旦 `f` 返回 `false`,执行流程**不一定会立即终止**,因此 `f` 可能还会被继续用于后续元素,且这些调用的返回值同样为 `false`。 + +--- + +=== 大小与容量 + +==== 空 + +```c++ [[nodiscard]] bool empty() const noexcept; ``` + +[horizontal] +返回值:;; `size() == 0`(容器为空时返回 true,否则返回 false) + +--- + +==== 大小 + +```c++ size_type size() const noexcept; ``` + +[horizontal] +返回值:;; 哈希表中的元素总数。 + +[horizontal] +注意:;; 在存在并发插入操作时,返回的值可能无法精确反映函数执行完成后哈希表的真实大小。 + +--- + +==== max_size + +```c++ size_type max_size() const noexcept; ``` + +[horizontal] +返回值:;; 哈希表能容纳的最大元素数量(最大容量)。 + +--- + +=== 修改器 + +==== 原地构造 +```c++ template bool emplace(Args&&... args); ``` + +当且仅当哈希表中不存在等价键的元素时,才会使用参数 `args` 构造对象并插入到哈希表中。 + +[horizontal] +要求:;; `value_type` 可由参数 `args` 构造。 +返回值:;; 成功插入元素时返回 `true`。 +并发特性:;; 当对当前对象执行重哈希操作时会阻塞。 +注意:;; 若 `args...` 格式为 `k,v`,则会仅使用参数 `k` 进行键检查,**直到确定需要插入元素时**,才会构造完整对象。 + +--- + +==== 复制插入 +```c++ bool insert(const value_type& obj); bool insert(const init_type& obj); ``` + +当且仅当哈希表中不存在键等价的元素时,将`obj`插入表中。 + +[horizontal] +要求:;; `value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^] 要求。返回值:;; 成功执行插入操作则返回 `true`。并发特性:;; 对当前对象执行重哈希操作时会发生阻塞。注意:;; 若调用形式为 `insert(x)`,且 `x` 可同时隐式转换为 `const value_type&` 与 `const init_type&`,该调用不存在歧义,会选用 `init_type` 重载版本。 + +--- + +==== 移动插入 +```c++ bool insert(value_type&& obj); bool insert(init_type&& obj); ``` + +当且仅当哈希表中不存在键等价的元素时,将`obj`插入表中。 + +[horizontal] +要求:;; `value_type` 满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^] 要求。 +返回值:;; 成功执行插入操作则返回 `true`。 +并发特性:;; 对当前对象执行重哈希操作时会发生阻塞。 +注意:;; 若调用形式为 `insert(x)`,且 `x` 可同时转换为 `value_type&&` 与 `init_type&&`,该调用不存在歧义,会选用 `init_type` 重载版本。 + +--- + +==== 迭代器范围插入 +```c++ template size_type insert(InputIterator first, InputIterator last); ``` + +等价于 [listing,subs="+macros,+quotes"] +----- + while(first != last) this->xref:#concurrent_node_map_emplace[emplace](*first++); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== 初始化列表插入 +```c++ size_type insert(std::initializer_list il); ``` + +等价于 [listing,subs="+macros,+quotes"] +----- + this->xref:#concurrent_node_map_insert_iterator_range[insert](il.begin(), il.end()); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== 节点插入 +```c++ insert_return_type insert(node_type&& nh); ``` + +若`nh`非空,则当且仅当哈希表中不存在与`nh.key()`等价的键时,将关联元素插入表中。函数返回时,`nh`为空。 + +[horizontal] +返回值:;; 由`inserted`和`node`构造的`insert_return_type`对象: +* 若 `nh` 为空,则 `inserted` 为 `false` 且 `node` 为空。 +* 若插入操作成功,则 `inserted` 为 true, 且 `node` 为空。 +* 若插入操作失败,则 `inserted` 为 false ,且 `node` 保留 `nh` 的原值。 +若 `nh` 非空,则当且仅当容器中不存在键等价于 `nh.key()` 的元素时,插入关联元素。函数返回时 `nh` 为空。 + +--- + +==== emplace_or_[c]visit +```c++ template bool emplace_or_visit(Args&&... args, F&& f); template bool emplace_or_cvisit(Args&&... args, F&& f); ``` + +若表中无等价键元素,则使用参数`args`构造对象并插入表中。否则,调用`f`并传入等价元素的引用;当且仅当使用`emplace_or_cvisit`时,该引用为常量引用。 + +[horizontal] +要求:;; `value_type` 可由参数 `args` 构造。 +返回值:;; 成功执行插入操作则返回 `true`。 +并发特性:;; 对当前对象执行重哈希操作时会发生阻塞。 +注意:;; 该接口仅为说明性接口,因为C++不允许在可变参数包之后声明参数 `f`。 + +--- + +==== 复制 insert++_++or++_[++c++]++visit +```c++ template bool insert_or_visit(const value_type& obj, F f); template bool insert_or_cvisit(const value_type& obj, F f); template bool insert_or_visit(const init_type& obj, F f); template bool insert_or_cvisit(const init_type& obj, F f); ``` + +当且仅当哈希表中不存在等价键的元素时,将`obj`插入表中。否则,调用`f`并传入等价元素的引用;当且仅当使用`*_cvisit`重载版本时,该引用为常量引用。 + +[horizontal] +要求:;; `value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^] 要求。 +返回值:;; 成功执行插入操作则返回 `true`。 +并发特性:;; 对当前对象执行重哈希操作时会发生阻塞。 +注意:;; 在形式为 `insert_or_[c]visit(obj, f)` 的调用中,仅当 `std::remove_cv::type>::type` 为 `value_type` 时,接受 `const value_type&` 参数的重载版本才会参与重载决议。 + +--- + +==== 移动 insert++_++or++_[++c++]++visit +```c++ template bool insert_or_visit(value_type&& obj, F f); template bool insert_or_cvisit(value_type&& obj, F f); template bool insert_or_visit(init_type&& obj, F f); template bool insert_or_cvisit(init_type&& obj, F f); ``` + +当且仅当哈希表中不存在等价键的元素时,将`obj`插入表中。否则,调用`f`并传入等价元素的引用;当且仅当使用`*_cvisit`重载版本时,该引用为常量引用。 + +[horizontal] +要求:;; `value_type` 满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^] 要求。 +返回值:;; 成功执行插入操作则返回 `true`。 +并发特性:;; 对当前对象执行重哈希操作时会发生阻塞。 +注意:;; 在形式为 `insert_or_[c]visit(obj, f)` 的调用中,仅当 `std::remove_reference::type` 为 `value_type` 时,接受 `value_type&&` 参数的重载版本才会参与重载决议。 + +--- + +==== 迭代器范围插入或访问 +```c++ template size_type insert_or_visit(InputIterator first, InputIterator last, F f); template size_type insert_or_cvisit(InputIterator first, InputIterator last, F f); ``` + +等价于 [listing,subs="+macros,+quotes"] +----- + while(first != last) this->xref:#concurrent_node_map_emplace_or_cvisit[emplace_or_[c\]visit](*first++, f); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== 初始化列表插入或访问 +```c++ template size_type insert_or_visit(std::initializer_list il, F f); template size_type insert_or_cvisit(std::initializer_list il, F f); ``` + +等价于 [listing,subs="+macros,+quotes"] +----- + this->xref:#concurrent_node_map_insert_iterator_range_or_visit[insert_or_[c\]visit](il.begin(), il.end(), std::ref(f)); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== 节点插入或访问 +```c++ template insert_return_type insert_or_visit(node_type&& nh, F f); template insert_return_type insert_or_cvisit(node_type&& nh, F f); ``` + +若`nh`为空,则不执行任何操作。否则,当且仅当哈希表中不存在与`nh.key()`等价的键时,将关联元素插入表中。若存在等价键,则调用`f`并传入等价元素的引用;当且仅当使用`insert_or_cvisit`时,该引用为常量引用。 + +[horizontal] +返回值:;; 由`inserted`和`node`构造的`insert_return_type`对象: +* 若 `nh` 为空,则 `inserted` 为 `false` 且 `node` 为空。 +* 若插入操作成功,则 `inserted` 为 true, 且 `node` 为空。 +* 若插入操作失败,则 `inserted` 为 false ,且 `node` 保留 `nh` 的原值。 +若 `nh` 为空,则不执行任何操作。否则,当且仅当容器中不存在键等价于 `nh.key()` 的元素时,插入其关联的元素;否则,以等价元素的引用为参数调用 `f` ;当且仅当使用 `insert++_++or++_++cvisit` 时该引用为常量引用。 + +--- + +==== emplace_and_[c]visit +```c++ template bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2); template bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2); ``` + +若表中无等价键元素,则使用参数`args`构造对象并插入表中,随后以非常量引用指向新创建的元素并调用`f1`。否则,以指向等价元素的引用调用`f2`;当且仅当使用`emplace_and_cvisit`时,该引用为常量引用。 + +[horizontal] +要求:;; `value_type` 可由 `args` 构造。 +返回值:;; 若执行插入操作则返回 `true`。 +并发特性:;; 阻塞对 `*this` 的重哈希操作。 +注意:;; 该接口仅作说明用途,因为 C++ 不允许在可变参数包之后声明参数 `f1` 和 `f2`。 + +--- + +==== 复制 insert++_++and++_[++c++]++visit +```c++ template bool insert_and_visit(const value_type& obj, F1 f1, F2 f2); template bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2); template bool insert_and_visit(const init_type& obj, F1 f1, F2 f2); template bool insert_and_cvisit(const init_type& obj, F1 f1, F2 f2); ``` + +当且仅当哈希表中不存在等价键的元素时,将`obj`插入表中,随后以非常量引用指向新创建的元素并调用`f1`。否则,以指向等价元素的引用调用`f2`;当且仅当使用`*_cvisit`重载版本时,该引用为常量引用。 + +[horizontal] +要求:;; `value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^] 要求。 +返回值:;; 若执行插入操作则返回 `true`。 +并发特性:;; 阻塞对 `*this` 的重哈希操作。 +注意:;; 在形式为 `insert_and_[c]visit(obj, f1, f2)` 的调用中,仅当 `std::remove_cv::type>::type` 为 `value_type` 时,接受 `const value_type&` 参数的重载版本才会参与重载决议。 + +--- + +==== 移动 insert++_++and++_[++c++]++visit +```c++ template bool insert_and_visit(value_type&& obj, F1 f1, F2 f2); template bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2); template bool insert_and_visit(init_type&& obj, F1 f1, F2 f2); template bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2); ``` + +当且仅当哈希表中不存在等价键的元素时,将`obj`插入表中,随后以非常量引用指向新创建的元素并调用`f1`。否则,以指向等价元素的引用调用`f2`;当且仅当使用`*_cvisit`重载版本时,该引用为常量引用。 + +[horizontal] +要求:;; `value_type` 满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^] 要求。 +返回值:;; 若执行插入操作则返回 `true`。 +并发特性:;; 阻塞对 `*this` 的重哈希操作。 +注意:;; 在形式为 `insert_and_[c]visit(obj, f1, f2)` 的调用中,仅当 `std::remove_reference::type` 为 `value_type` 时,接受 `value_type&&` 参数的重载版本才会参与重载决议。 + +--- + +==== 迭代器范围插入并访问 +```c++ template size_type insert_or_visit(InputIterator first, InputIterator last, F1 f1, F2 f2); template size_type insert_or_cvisit(InputIterator first, InputIterator last, F1 f2, F2 f2); ``` + +等价于 [listing,subs="+macros,+quotes"] +----- + while(first != last) this->xref:#concurrent_node_map_emplace_and_cvisit[emplace_and_[c\]visit](*first++, f1, f2); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== 初始化列表插入并访问 +```c++ template size_type insert_and_visit(std::initializer_list il, F1 f1, F2 f2); template size_type insert_and_cvisit(std::initializer_list il, F1 f1, F2 f2); ``` + +等价于 [listing,subs="+macros,+quotes"] +----- + this->xref:#concurrent_node_map_insert_iterator_range_and_visit[insert_and_[c\]visit](il.begin(), il.end(), std::ref(f1), std::ref(f2)); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== 节点插入并访问 +```c++ template insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2); template insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2); ``` + +若`nh`为空,则不执行任何操作。否则,当且仅当哈希表中不存在与`nh.key()`等价的键时,将关联元素插入表中,随后以非常量引用指向新插入的元素并调用`f1`。若存在等价键,则调用`f2`并传入等价元素的引用;当且仅当使用`insert_or_cvisit`时,该引用为常量引用。 + +[horizontal] +返回值:;; 由`inserted`和`node`构造的`insert_return_type`对象: +* 若 `nh` 为空,则 `inserted` 为 `false` 且 `node` 为空。 +* 若插入操作成功,则 `inserted` 为 true, 且 `node` 为空。 +* 若插入操作失败,则 `inserted` 为 false ,且 `node` 保留 `nh` 的原值。 +若 `nh` 为空,则不执行任何操作。否则,当且仅当容器中不存在键等价于 `nh.key()` 的元素时,插入关联元素,并以新插入元素的非常量引用为参数调用 `f1` ;否则,以等价元素的引用为参数调用 `f2` ;当且仅当使用 `insert++_++or++_++cvisit` (此处应为 `insert++_++and++_++cvisit` ,可能是英文出了点错误)时该引用为常量引用。 + +--- + +==== try_emplace +```c++ template bool try_emplace(const key_type& k, Args&&... args); template bool try_emplace(key_type&& k, Args&&... args); template bool try_emplace(K&& k, Args&&... args); ``` + +若表中不存在键为`k`的元素,则将由`k`和`args`构造的元素插入表中。 + +[horizontal] +返回值:;; 若执行插入操作则返回 true。 +并发特性:;; 阻塞对 *this 的重哈希操作。 +注意:;; 该函数与 xref:#concurrent_node_map_emplace [emplace] 类似,区别在于:若表中已存在等价键的元素,则不会构造 value_type;否则,构造方式如下: +// first two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) + +// third overload +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) ``` + +与 xref:#concurrent_node_map_emplace[emplace] 不同,该函数会直接将所有参数转发给 `value_type` 的构造函数。 + +仅当 Hash::is_transparent 与 Pred::is_transparent 为合法的成员类型别名时,template 这个重载版本才会参与重载决议。 +库实现假定 Hash 可同时接受 K 类型与 Key 类型作为参数,且 Pred 是透明的。 +这一特性支持异构查找,从而避免实例化 Key 类型对象带来的性能开销。 + +-- + +--- + +==== try_emplace_or_[c]visit +```c++ template bool try_emplace_or_visit(const key_type& k, Args&&... args, F&& f); template bool try_emplace_or_cvisit(const key_type& k, Args&&... args, F&& f); template bool try_emplace_or_visit(key_type&& k, Args&&... args, F&& f); template bool try_emplace_or_cvisit(key_type&& k, Args&&... args, F&& f); template bool try_emplace_or_visit(K&& k, Args&&... args, F&& f); template bool try_emplace_or_cvisit(K&& k, Args&&... args, F&& f); ``` + +若表中不存在键为`k`的元素,则将由`k`和`args`构造的元素插入表中。 +否则,以指向等价元素的引用调用`f`;当且仅当使用`*_cvisit`重载版本时,该引用为常量引用。 + +[horizontal] +返回值:;; 若执行插入操作则返回 true。 +并发特性:;; 阻塞对 *this 的重哈希操作。 +注意:;; 若已存在等价键的元素,则不会构造 value_type;否则,构造方式如下: +// first four overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) + +// last two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) ``` + +该接口仅为说明性用法,因为 C++ 不允许在可变参数包之后声明参数 `f`。 + +仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型别名时,`template` 这些重载版本才会参与重载决议。库假定 `Hash` 可同时接受 `K` 类型和 `Key` 类型作为参数,且 `Pred` 是透明的。这支持**异构查找**,从而避免实例化 `Key` 类型对象带来的性能开销。 + +-- + +--- + +==== try_emplace_and_[c]visit +```c++ template bool try_emplace_and_visit(const key_type& k, Args&&... args, F1&& f1, F2&& f2); template bool try_emplace_and_cvisit(const key_type& k, Args&&... args, F1&& f1, F2&& f2); template bool try_emplace_and_visit(key_type&& k, Args&&... args, F1&& f1, F2&& f2); template bool try_emplace_and_cvisit(key_type&& k, Args&&... args, F1&& f1, F2&& f2); template bool try_emplace_and_visit(K&& k, Args&&... args, F1&& f1, F2&& f2); template bool try_emplace_and_cvisit(K&& k, Args&&... args, F1&& f1, F2&& f2); ``` + +若表中不存在键为`k`的元素,则将由`k`和`args`构造的元素插入表中,随后以非常量引用指向新创建的元素并调用`f1`。 +否则,以指向等价元素的引用调用`f2`;当且仅当使用`*_cvisit`重载版本时,该引用为常量引用。 + +[horizontal] +返回值:;; 若执行插入操作则返回 true。 +并发特性:;; 阻塞对 *this 的重哈希操作。 +注意:;; 若已存在等价键的元素,则不会构造 value_type;否则,构造方式如下: +// first four overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) + +// last two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) ``` + +该接口仅为说明性用法,因为 C++ 不允许在可变参数包之后声明 `f1` 和 `f2` 参数。 + +仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为有效成员类型别名时,`template` 这些重载版本才会参与重载决议。 +库假定 `Hash` 可同时接受 `K` 类型与 `Key` 类型作为参数,且 `Pred` 是透明的。 +这支持**异构查找**,从而避免实例化 `Key` 类型对象带来的性能开销。 + +-- + +--- + +==== insert_or_assign +```c++ template bool insert_or_assign(const key_type& k, M&& obj); template bool insert_or_assign(key_type&& k, M&& obj); template bool insert_or_assign(K&& k, M&& obj); ``` + +向表中插入新元素,或通过为已包含的值赋值来更新现有元素。 + +若表中存在键为`k`的元素,则通过`std::forward(obj)`赋值来更新该元素。 + +若不存在该元素,则以如下形式添加到表中: +// first two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(obj))) + +// third overload +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(obj))) ``` + +[horizontal] +返回值:;; 若执行插入操作则返回 `true`。 +并发特性:;; 阻塞对 `*this` 的重哈希操作。 +注意:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型别名时,`template` 才参与重载决议。库假定 `Hash` 可同时接受 `K` 类型与 `Key` 类型作为参数,且 `Pred` 是透明的。这支持异构查找,从而避免实例化 `Key` 类型对象带来的性能开销。 + +--- + +==== 擦除 +```c++ size_type erase(const key_type& k); template size_type erase(const K& k); ``` + +若存在键与 `k` 等价的元素,则删除该元素。 + +[horizontal] +返回值:;; 删除的元素数量(0 或 1)。 +抛出异常:;; 仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +注意:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型别名时,`template` 重载版本才参与重载决议。库假定 `Hash` 可同时接受 `K` 类型与 `Key` 类型作为参数,且 `Pred` 是透明的。这支持异构查找,从而避免实例化 `Key` 类型对象带来的性能开销。 + +--- + +==== erase_if by Key +```c++ template size_type erase_if(const key_type& k, F f); template size_type erase_if(const K& k, F f); ``` + +若存在键与 `k` 等价的元素 `x`,且 `f(x)` 返回 `true`,则删除该元素。 + +[horizontal] +返回值:;; 删除的元素数量(0 或 1)。 +抛出异常:;; 仅当 `hasher`、`key_equal` 或 `f` 抛出异常时才会抛出。 +注意:;; `f` 接收指向 `x` 的非常量引用。 +`template` 重载版本仅在 `std::is_execution_policy_v>` 为 `false` 时参与重载决议。 +`template` 重载版本仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 为有效成员类型别名时参与重载决议。库假定 `Hash` 可同时接受 `K` 类型与 `Key` 类型作为参数,且 `Pred` 是透明的。这支持异构查找,从而避免实例化 `Key` 类型对象带来的性能开销。 + +--- + +==== erase_if +```c++ template size_type erase_if(F f); ``` + +依次以非常量引用为参数,对表中的每个元素调用 `f`,并删除所有 `f` 返回 `true` 的元素。 + +[horizontal] +返回值:;; 删除的元素总数。 +抛出异常:;; 仅当 `f` 抛出异常时才会抛出。 + +--- + +==== 并行条件擦除 +```c++ template void erase_if(ExecutionPolicy&& policy, F f); ``` + +根据指定执行策略的语义并行执行操作:依次以**非常量引用**为参数对表中的每个元素调用 `f`,并删除所有 `f` 返回 `true` 的元素。 + +[horizontal] +抛出异常:;; 依据所使用执行策略的异常处理机制,若 `f` 内部抛出异常,可能会调用 `std::terminate`。注意:;; 仅在支持 C++17 并行算法的编译器中可用。该重载版本仅在`std::is_execution_policy_v>` 为 `true` 时参与重载决议。不允许使用无序执行策略。 + +--- + +==== 交换 +```c++ void swap(concurrent_node_map& other) noexcept(boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_swap::value); ``` + +将当前表与参数表的内容进行交换。 + +若声明了 `Allocator::propagate_on_container_swap` 且其值为 `true`,则交换两个表的分配器;否则,使用不相等的分配器进行交换会导致未定义行为。 + +[horizontal] +抛出异常:;; 除非 `key_equal` 或 `hasher` 在交换时抛出异常,否则不抛出任何异常。 +并发特性:;; 阻塞当前容器 `*this` 与目标容器 `other`。 + +--- + +==== extract +```c++ node_type extract(const key_type& k); template node_type extract(K&& k); ``` + +若存在键与 `k` 等价的元素,则提取该元素。 + +[horizontal] +返回值:;; 一个存储被提取元素的 `node_type` 对象;若未提取任何元素,则为空。抛出异常:;; 仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。注意:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型别名时,`template` 重载版本才参与重载决议。库假定 `Hash` 可同时接受 `K` 类型与 `Key` 类型作为参数,且 `Pred` 是透明的。这支持异构查找,从而避免实例化 `Key` 类型对象带来的性能开销。 + +--- + +==== 条件提取 +```c++ template node_type extract_if(const key_type& k, F f); template node_type extract_if(K&& k, F f); ``` + +若存在键与 `k` 等价的元素 `x`,且 `f(x)` 返回 `true`,则提取该元素。 + +[horizontal] +返回值:;; 一个存储被提取元素的 `node_type` 对象;若未提取任何元素,则为空。 +抛出异常:;; 仅当 `hasher`、`key_equal` 或 `f` 抛出异常时才会抛出。 +注意:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型别名时,`template` 重载版本才参与重载决议。库假定 `Hash` 可同时接受 `K` 类型与 `Key` 类型作为参数,且 `Pred` 是透明的。这支持异构查找,从而避免实例化 `Key` 类型对象带来的性能开销。 + +--- + +==== 清空 +```c++ void clear() noexcept; ``` + +清空表中的所有元素。 + +[horizontal] +后置条件:;; `size() == 0`,且 `max_load() >= max_load_factor() * bucket_count()` +并发特性:;; 阻塞当前容器 `*this`。 + +--- + +==== 合并 +```c++ template size_type merge(concurrent_node_map& source); template size_type merge(concurrent_node_map&& source); ``` + +将源容器 `source` 中所有**键不存在于当前容器 `*this`** 中的元素**移动插入**到当前容器,并从 `source` 中删除这些元素。 + +[horizontal] +返回值:;; 插入的元素数量。 +并发特性:;; 阻塞当前容器 `*this` 与源容器 `source`。 + +--- + +=== 观察器 + +==== get++_++allocator +``` allocator_type get_allocator() const noexcept; ``` + +[horizontal] +返回值:;; 表的分配器。 + +--- + +==== 哈希函数 +``` hasher hash_function() const; ``` + +[horizontal] +返回值:;; 表的哈希函数。 + +--- + +==== key_eq +``` key_equal key_eq() const; ``` + +[horizontal] +返回值:;; 表的键相等性谓词。 + +--- + +=== 映射操作 + +==== count +```c++ size_type count(const key_type& k) const; template size_type count(const K& k) const; ``` + +[horizontal] +返回值:;; 与键 `k` 等价的元素数量(值为 0 或 1)。 +注意:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型别名时,`template` 重载版本才参与重载决议。库假定 `Hash` 可同时接受 `K` 类型与 `Key` 类型作为参数,且 `Pred` 是透明的。这支持异构查找,从而避免实例化 `Key` 类型对象带来的性能开销。 +在存在并发插入操作的情况下,返回的值可能无法精确反映执行后容器的真实状态。 + +--- + +==== 包含 +```c++ bool contains(const key_type& k) const; template bool contains(const K& k) const; ``` + +[horizontal] +返回值:;; 布尔值,表示表中是否存在键等于 `k` 的元素。 +注意:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型别名时,`template` 重载版本才参与重载决议。库假定 `Hash` 可同时接受 `K` 类型与 `Key` 类型作为参数,且 `Pred` 是透明的。这支持异构查找,从而避免实例化 `Key` 类型对象带来的性能开销。 +在存在并发插入操作的情况下,返回的值可能无法精确反映执行后容器的真实状态。 + +--- +=== 桶接口 + +==== bucket_count +```c++ size_type bucket_count() const noexcept; ``` + +[horizontal] +返回值:;; 桶数组的大小。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ float load_factor() const noexcept; ``` + +[horizontal] +返回值:;; `static_cast(size()) / static_cast(bucket_count())`;若 `bucket_count() == 0`,则返回 `0`。 + +--- + +==== max_load_factor(最大负载因子) + +```c++ float max_load_factor() const noexcept; ``` + +[horizontal] +返回值:;; 容器的最大负载因子。 + +--- + +==== 设置最大负载因子 +```c++ void max_load_factor(float z); ``` + +[horizontal] +效果:;; 不执行任何操作,因为不允许用户修改此参数。保留该函数是为了与 `boost::unordered_map` 保持兼容。 + +--- + + +==== max_load(最大负载) + +```c++ size_type max_load() const noexcept; ``` + +[horizontal] +返回值:;; 表在不进行重哈希的情况下可容纳的最大元素数量(假设不会再删除任何元素)。 +注意:;; 构造、重哈希或清空后,表的最大装载量至少为 `max_load_factor() * bucket_count()`。在高负载条件下执行删除操作时,该数值可能会降低。 +在存在并发插入操作的情况下,返回的值可能无法精确反映执行后容器的真实状态。 + +--- + +==== 重哈希 +```c++ void rehash(size_type n); ``` + +效果:;; 必要时调整桶数组的大小,使桶数量至少为 `n`,且负载因子小于等于最大负载因子。适用时,该操作会增大或缩小容器的桶数量 `bucket_count()`。 + +效果:;; 当容器元素数量 `size() == 0` 时,调用 `rehash(0)` 会释放底层桶数组的内存。 + +[horizontal] +抛出异常:;; 若抛出异常,函数不会产生任何效果(由容器的哈希函数或比较函数抛出的异常除外)。 +并发特性:;; 阻塞当前容器 `*this`。 + +==== 保留 +```c++ void reserve(size_type n); ``` + +效果:;; 等价于 `a.rehash(ceil(n / a.max_load_factor()))`。 + +效果:;; 与 `rehash` 功能类似,该函数可用于增大或缩小容器的桶数量。 + +[horizontal] +抛出异常:;; 若抛出异常,函数不会产生任何效果(由容器的哈希函数或比较函数抛出的异常除外)。 +并发特性:;; 阻塞当前容器 `*this`。 + +--- + +=== 统计信息 + +==== get_stats +```c++ stats get_stats() const; ``` + +[horizontal] +返回值:;; 表截至目前执行的插入和查找操作的统计信息描述。 +注意:;; 仅当启用了**统计信息计算**功能时可用。 + +--- + +==== reset_stats +```c++ void reset_stats() noexcept; ``` + +[horizontal] +效果:;; 将容器维护的内部统计数据归零。 +注意:;; 仅当 xref:reference/stats.adoc#stats[statistics calculation] 被 xref:concurrent_node_map_boost_unordered_enable_stats[enabled] 启用时可用。 + +--- + +=== 推导指引 +如果以下任何一条件为真,则推导指引将不参与重载决议: + +- 该推导指引包含 `InputIterator` 模板参数,且为此参数推导出的类型不符合输入迭代器的要求。 - 该推导指引包含 `Allocator` 模板参数,且为该参数推导出的类型不符合分配器要求。 - 该推导指引包含 `Hash` 模板参数,且为该参数推导出的类型为整型或符合分配器要求。 - 该推导指引包含 `Pred` 模板参数,且为该参数推导出的类型符合分配器要求。 + +推导指引中的 `size++_++type` 参数类型,指向由该推导指引所推导容器类型的 `size++_++type` 成员类型。其默认值与所选构造函数的默认值一致。 + +==== _iter-value-type_ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-value-type__ = + typename std::iterator_traits::value_type; // exposition only +----- + +==== __iter-key-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-key-type__ = std::remove_const_t< + std::tuple_element_t<0, xref:#concurrent_node_map_iter_value_type[__iter-value-type__]>>; // exposition only +----- + +==== __iter-mapped-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-mapped-type__ = + std::tuple_element_t<1, xref:#concurrent_node_map_iter_value_type[__iter-value-type__]>; // exposition only +----- + +==== __iter-to-alloc-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-to-alloc-type__ = std::pair< + std::add_const_t>>, + std::tuple_element_t<1, xref:#concurrent_node_map_iter_value_type[__iter-value-type__]>>; // exposition only +----- + +=== 相等性比较 + +==== operator +```c++ template bool operator==(const concurrent_node_map& x, const concurrent_node_map& y); ``` + +若 `x.size() == y.size()`,且对于 `x` 中的每个元素,`y` 中均存在一个键相同、值相等(使用 `operator==` 比较值类型)的元素,则返回 `true`。 + +[horizontal] +并发特性:;; 阻塞 `x` 与 `y`。 +注意:;; 若两个表的相等性谓词不一致,行为未定义。 + +--- + +==== operator! +```c++ template bool operator!=(const concurrent_node_map& x, const concurrent_node_map& y); ``` + +若`x.size() == y.size()`,且`x`中每个元素在`y`中都存在键相同、值相等(使用`operator==`比较值类型)的对应元素,则返回`false`。 + +[horizontal] +并发特性:;; 阻塞 `x` 与 `y`。 +注意:;; 若两个表的相等性谓词不一致,行为未定义。 + +--- + +=== 交换 +```c++ template void swap(concurrent_node_map& x, concurrent_node_map& y) noexcept(noexcept(x.swap(y))); ``` + +等价于 [listing,subs="+macros,+quotes"] +----- +x.xref:#concurrent_node_map_swap[swap](y); +----- + +--- + +=== erase_if +```c++ template typename concurrent_node_map::size_type erase_if(concurrent_node_map& c, Predicate pred); ``` + +等价于 [listing,subs="+macros,+quotes"] +----- +c.xref:#concurrent_node_map_erase_if[erase_if](pred); +----- + +=== 序列化 + +`concurrent++_++node++_++map` 可通过本库提供的 API,借助 link:../../../../../serialization/index.html[Boost.Serialization] 实现归档/检索功能。同时支持常规格式与 XML 格式的归档文件。 + +==== 将 concurrent++_++node++_++map 保存至归档 + +将 `concurrent++_++node++_++map` 容器 `x` 中的所有元素保存至归档(XML 格式归档) `ar` 中。 + +[horizontal] +要求;; `std::remove++_++const++<++key++_++type++>++::type` 和 `std::remove++_++const++<++mapped++_++type++>++::type` 必须满足可序列化要求(XML 可序列化),且需要支持 Boost.Serialization 的 `save++_++construct++_++data` / `load++_++construct++_++data` 协议(该协议自动支持 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求)。 并发性;; 阻塞于 `x` 。 + +--- + +==== 从归档加载 concurrent++_++node++_++map + +删除 `concurrent++_++node++_++map` 容器 `x` 中的所有现有元素,并从归档(XML 归档) `ar` 插入原始 `concurrent++_++node++_++map` 容器 `other` 的元素副本,这些副本保存在 `ar` 读取的存储中。 + +[horizontal] +要求;; `x.key++_++equal()` 需要在功能上等价于 `other.key++_++equal()` 。 并发性;; 阻塞于 `x` 。 diff --git a/doc/modules/ROOT/pages/reference/concurrent_node_set_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/concurrent_node_set_zh_Hans.adoc new file mode 100644 index 0000000..c4c6f61 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/concurrent_node_set_zh_Hans.adoc @@ -0,0 +1,1369 @@ +[#concurrent_node_set] +== 类模板 concurrent++_++node++_++set + +:idprefix: concurrent_node_set_ + +`boost::concurrent++_++node++_++set` —— 一种基于节点的哈希表,用于存储唯一值,并允许在无外部同步机制的情况下并发执行元素插入、擦除、查找和访问操作。 + +尽管 `boost::concurrent++_++node++_++set` 的行为类似于容器,但它并不符合标准 C{plus}{plus} https://en.cppreference.com/w/cpp/named_req/Container[容器] 概念。具体而言,它不提供迭代器及相关操作(如 `begin` 、 `end` 等)。元素的访问通过用户提供的 _访问函数_ 实现,这些函数被传递至 `concurrent++_++node++_++set` 的内部操作,并以受控方式执行。这种基于访问的 API 设计支持低竞争度的并发使用场景。 + +`boost::concurrent++_++node++_++set` 的内部数据结构与 `boost::unordered++_++node++_++set` 类似。与 `boost::concurrent++_++flat++_++set` 不同,它提供了指针稳定性和节点处理功能,但可能以性能降低为代价。 + +=== 概要 + +[listing,subs="+macros,+quotes"] +----- +// #include xref:reference/header_concurrent_node_set.adoc[``] + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator> + class concurrent_node_set { + public: + // types + using key_type = Key; + using value_type = Key; + using init_type = Key; + using hasher = Hash; + using key_equal = Pred; + using allocator_type = Allocator; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + using node_type = _implementation-defined_; + using insert_return_type = _implementation-defined_; + + using stats = xref:reference/stats.adoc#stats_stats_type[__stats-type__]; // if statistics are xref:concurrent_node_set_boost_unordered_enable_stats[enabled] + + // constants + static constexpr size_type xref:#concurrent_node_set_constants[bulk_visit_size] = _implementation-defined_; + + // construct/copy/destroy + xref:#concurrent_node_set_default_constructor[concurrent_node_set](); + explicit xref:#concurrent_node_set_bucket_count_constructor[concurrent_node_set](size_type n, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + template + xref:#concurrent_node_set_iterator_range_constructor[concurrent_node_set](InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#concurrent_node_set_copy_constructor[concurrent_node_set](const concurrent_node_set& other); + xref:#concurrent_node_set_move_constructor[concurrent_node_set](concurrent_node_set&& other); + template + xref:#concurrent_node_set_iterator_range_constructor_with_allocator[concurrent_node_set](InputIterator f, InputIterator l,const allocator_type& a); + explicit xref:#concurrent_node_set_allocator_constructor[concurrent_node_set](const Allocator& a); + xref:#concurrent_node_set_copy_constructor_with_allocator[concurrent_node_set](const concurrent_node_set& other, const Allocator& a); + xref:#concurrent_node_set_move_constructor_with_allocator[concurrent_node_set](concurrent_node_set&& other, const Allocator& a); + xref:#concurrent_node_set_move_constructor_from_unordered_node_set[concurrent_node_set](unordered_node_set&& other); + xref:#concurrent_node_set_initializer_list_constructor[concurrent_node_set](std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#concurrent_node_set_bucket_count_constructor_with_allocator[concurrent_node_set](size_type n, const allocator_type& a); + xref:#concurrent_node_set_bucket_count_constructor_with_hasher_and_allocator[concurrent_node_set](size_type n, const hasher& hf, const allocator_type& a); + template + xref:#concurrent_node_set_iterator_range_constructor_with_bucket_count_and_allocator[concurrent_node_set](InputIterator f, InputIterator l, size_type n, + const allocator_type& a); + template + xref:#concurrent_node_set_iterator_range_constructor_with_bucket_count_and_hasher[concurrent_node_set](InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); + xref:#concurrent_node_set_initializer_list_constructor_with_allocator[concurrent_node_set](std::initializer_list il, const allocator_type& a); + xref:#concurrent_node_set_initializer_list_constructor_with_bucket_count_and_allocator[concurrent_node_set](std::initializer_list il, size_type n, + const allocator_type& a); + xref:#concurrent_node_set_initializer_list_constructor_with_bucket_count_and_hasher_and_allocator[concurrent_node_set](std::initializer_list il, size_type n, const hasher& hf, + const allocator_type& a); + xref:#concurrent_node_set_destructor[~concurrent_node_set](); + concurrent_node_set& xref:#concurrent_node_set_copy_assignment[operator++=++](const concurrent_node_set& other); + concurrent_node_set& xref:#concurrent_node_set_move_assignment[operator++=++](concurrent_node_set&& other) + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_move_assignment::value); + concurrent_node_set& xref:#concurrent_node_set_initializer_list_assignment[operator++=++](std::initializer_list); + allocator_type xref:#concurrent_node_set_get_allocator[get_allocator]() const noexcept; + + + // visitation + template size_t xref:#concurrent_node_set_cvisit[visit](const key_type& k, F f); + template size_t xref:#concurrent_node_set_cvisit[visit](const key_type& k, F f) const; + template size_t xref:#concurrent_node_set_cvisit[cvisit](const key_type& k, F f) const; + template size_t xref:#concurrent_node_set_cvisit[visit](const K& k, F f); + template size_t xref:#concurrent_node_set_cvisit[visit](const K& k, F f) const; + template size_t xref:#concurrent_node_set_cvisit[cvisit](const K& k, F f) const; + + template + size_t xref:concurrent_node_set_bulk_visit[visit](FwdIterator first, FwdIterator last, F f); + template + size_t xref:concurrent_node_set_bulk_visit[visit](FwdIterator first, FwdIterator last, F f) const; + template + size_t xref:concurrent_node_set_bulk_visit[cvisit](FwdIterator first, FwdIterator last, F f) const; + + template size_t xref:#concurrent_node_set_cvisit_all[visit_all](F f); + template size_t xref:#concurrent_node_set_cvisit_all[visit_all](F f) const; + template size_t xref:#concurrent_node_set_cvisit_all[cvisit_all](F f) const; + template + void xref:#concurrent_node_set_parallel_cvisit_all[visit_all](ExecutionPolicy&& policy, F f); + template + void xref:#concurrent_node_set_parallel_cvisit_all[visit_all](ExecutionPolicy&& policy, F f) const; + template + void xref:#concurrent_node_set_parallel_cvisit_all[cvisit_all](ExecutionPolicy&& policy, F f) const; + + template bool xref:#concurrent_node_set_cvisit_while[visit_while](F f); + template bool xref:#concurrent_node_set_cvisit_while[visit_while](F f) const; + template bool xref:#concurrent_node_set_cvisit_while[cvisit_while](F f) const; + template + bool xref:#concurrent_node_set_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f); + template + bool xref:#concurrent_node_set_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f) const; + template + bool xref:#concurrent_node_set_parallel_cvisit_while[cvisit_while](ExecutionPolicy&& policy, F f) const; + + // capacity + ++[[nodiscard]]++ bool xref:#concurrent_node_set_empty[empty]() const noexcept; + size_type xref:#concurrent_node_set_size[size]() const noexcept; + size_type xref:#concurrent_node_set_max_size[max_size]() const noexcept; + + // modifiers + template bool xref:#concurrent_node_set_emplace[emplace](Args&&... args); + bool xref:#concurrent_node_set_copy_insert[insert](const value_type& obj); + bool xref:#concurrent_node_set_move_insert[insert](value_type&& obj); + template bool xref:#concurrent_node_set_transparent_insert[insert](K&& k); + template size_type xref:#concurrent_node_set_insert_iterator_range[insert](InputIterator first, InputIterator last); + size_type xref:#concurrent_node_set_insert_initializer_list[insert](std::initializer_list il); + insert_return_type xref:#concurrent_node_set_insert_node[insert](node_type&& nh); + + template bool xref:#concurrent_node_set_emplace_or_cvisit[emplace_or_visit](Args&&... args, F&& f); + template bool xref:#concurrent_node_set_emplace_or_cvisit[emplace_or_cvisit](Args&&... args, F&& f); + template bool xref:#concurrent_node_set_copy_insert_or_cvisit[insert_or_visit](const value_type& obj, F f); + template bool xref:#concurrent_node_set_copy_insert_or_cvisit[insert_or_cvisit](const value_type& obj, F f); + template bool xref:#concurrent_node_set_move_insert_or_cvisit[insert_or_visit](value_type&& obj, F f); + template bool xref:#concurrent_node_set_move_insert_or_cvisit[insert_or_cvisit](value_type&& obj, F f); + template bool xref:#concurrent_node_set_transparent_insert_or_cvisit[insert_or_visit](K&& k, F f); + template bool xref:#concurrent_node_set_transparent_insert_or_cvisit[insert_or_cvisit](K&& k, F f); + template + size_type xref:#concurrent_node_set_insert_iterator_range_or_visit[insert_or_visit](InputIterator first, InputIterator last, F f); + template + size_type xref:#concurrent_node_set_insert_iterator_range_or_visit[insert_or_cvisit](InputIterator first, InputIterator last, F f); + template size_type xref:#concurrent_node_set_insert_initializer_list_or_visit[insert_or_visit](std::initializer_list il, F f); + template size_type xref:#concurrent_node_set_insert_initializer_list_or_visit[insert_or_cvisit](std::initializer_list il, F f); + template insert_return_type xref:#concurrent_node_set_insert_node_or_visit[insert_or_visit](node_type&& nh, F f); + template insert_return_type xref:#concurrent_node_set_insert_node_or_visit[insert_or_cvisit](node_type&& nh, F f); + + template + bool xref:#concurrent_node_set_emplace_and_cvisit[emplace_and_visit](Args&&... args, F1&& f1, F2&& f2); + template + bool xref:#concurrent_node_set_emplace_and_cvisit[emplace_and_cvisit](Args&&... args, F1&& f1, F2&& f2); + template bool xref:#concurrent_node_set_copy_insert_and_cvisit[insert_and_visit](const value_type& obj, F1 f1, F2 f2); + template bool xref:#concurrent_node_set_copy_insert_and_cvisit[insert_and_cvisit](const value_type& obj, F1 f1, F2 f2); + template bool xref:#concurrent_node_set_move_insert_and_cvisit[insert_and_visit](value_type&& obj, F1 f1, F2 f2); + template bool xref:#concurrent_node_set_move_insert_and_cvisit[insert_and_cvisit](value_type&& obj, F1 f1, F2 f2); + template bool xref:#concurrent_node_set_transparent_insert_and_cvisit[insert_and_visit](K&& k, F1 f1, F2 f2); + template bool xref:#concurrent_node_set_transparent_insert_and_cvisit[insert_and_cvisit](K&& k, F1 f1, F2 f2); + template + size_type xref:#concurrent_node_set_insert_iterator_range_and_visit[insert_and_visit](InputIterator first, InputIterator last, F1 f1, F2 f2); + template + size_type xref:#concurrent_node_set_insert_iterator_range_and_visit[insert_and_cvisit](InputIterator first, InputIterator last, F1 f1, F2 f2); + template + size_type xref:#concurrent_node_set_insert_initializer_list_and_visit[insert_and_visit](std::initializer_list il, F1 f1, F2 f2); + template + size_type xref:#concurrent_node_set_insert_initializer_list_and_visit[insert_and_cvisit](std::initializer_list il, F1 f1, F2 f2); + template + insert_return_type xref:#concurrent_node_set_insert_node_and_visit[insert_and_visit](node_type&& nh, F1 f1, F2 f2); + template + insert_return_type xref:#concurrent_node_set_insert_node_and_visit[insert_and_cvisit](node_type&& nh, F1 f1, F2 f2); + + size_type xref:#concurrent_node_set_erase[erase](const key_type& k); + template size_type xref:#concurrent_node_set_erase[erase](const K& k); + + template size_type xref:#concurrent_node_set_erase_if_by_key[erase_if](const key_type& k, F f); + template size_type xref:#concurrent_node_set_erase_if_by_key[erase_if](const K& k, F f); + template size_type xref:#concurrent_node_set_erase_if[erase_if](F f); + template void xref:#concurrent_node_set_parallel_erase_if[erase_if](ExecutionPolicy&& policy, F f); + + void xref:#concurrent_node_set_swap[swap](concurrent_node_set& other) + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_swap::value); + + node_type xref:#concurrent_node_set_extract[extract](const key_type& k); + template node_type xref:#concurrent_node_set_extract[extract](const K& k); + + template node_type xref:#concurrent_node_set_extract_if[extract_if](const key_type& k, F f); + template node_type xref:#concurrent_node_set_extract[extract_if](const K& k, F f); + + void xref:#concurrent_node_set_clear[clear]() noexcept; + + template + size_type xref:#concurrent_node_set_merge[merge](concurrent_node_set& source); + template + size_type xref:#concurrent_node_set_merge[merge](concurrent_node_set&& source); + + // observers + hasher xref:#concurrent_node_set_hash_function[hash_function]() const; + key_equal xref:#concurrent_node_set_key_eq[key_eq]() const; + + // set operations + size_type xref:#concurrent_node_set_count[count](const key_type& k) const; + template + size_type xref:#concurrent_node_set_count[count](const K& k) const; + bool xref:#concurrent_node_set_contains[contains](const key_type& k) const; + template + bool xref:#concurrent_node_set_contains[contains](const K& k) const; + + // bucket interface + size_type xref:#concurrent_node_set_bucket_count[bucket_count]() const noexcept; + + // hash policy + float xref:#concurrent_node_set_load_factor[load_factor]() const noexcept; + float xref:#concurrent_node_set_max_load_factor[max_load_factor]() const noexcept; + void xref:#concurrent_node_set_set_max_load_factor[max_load_factor](float z); + size_type xref:#concurrent_node_set_max_load[max_load]() const noexcept; + void xref:#concurrent_node_set_rehash[rehash](size_type n); + void xref:#concurrent_node_set_reserve[reserve](size_type n); + + // statistics (if xref:concurrent_node_set_boost_unordered_enable_stats[enabled]) + stats xref:#concurrent_node_set_get_stats[get_stats]() const; + void xref:#concurrent_node_set_reset_stats[reset_stats]() noexcept; + }; + + // Deduction Guides + template>, + class Pred = std::equal_to>, + class Allocator = std::allocator>> + concurrent_node_set(InputIterator, InputIterator, typename xref:#concurrent_node_set_deduction_guides[__see below__]::size_type = xref:#concurrent_node_set_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> concurrent_node_set, Hash, Pred, Allocator>; + + template, class Pred = std::equal_to, + class Allocator = std::allocator> + concurrent_node_set(std::initializer_list, typename xref:#concurrent_node_set_deduction_guides[__see below__]::size_type = xref:#concurrent_node_set_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> concurrent_node_set; + + template + concurrent_node_set(InputIterator, InputIterator, typename xref:#concurrent_node_set_deduction_guides[__see below__]::size_type, Allocator) + -> concurrent_node_set, + boost::hash>, + std::equal_to>, Allocator>; + + template + concurrent_node_set(InputIterator, InputIterator, Allocator) + -> concurrent_node_set, + boost::hash>, + std::equal_to>, Allocator>; + + template + concurrent_node_set(InputIterator, InputIterator, typename xref:#concurrent_node_set_deduction_guides[__see below__]::size_type, Hash, + Allocator) + -> concurrent_node_set, Hash, + std::equal_to>, Allocator>; + + template + concurrent_node_set(std::initializer_list, typename xref:#concurrent_node_set_deduction_guides[__see below__]::size_type, Allocator) + -> concurrent_node_set, std::equal_to, Allocator>; + + template + concurrent_node_set(std::initializer_list, Allocator) + -> concurrent_node_set, std::equal_to, Allocator>; + + template + concurrent_node_set(std::initializer_list, typename xref:#concurrent_node_set_deduction_guides[__see below__]::size_type, Hash, Allocator) + -> concurrent_node_set, Allocator>; + +} // namespace unordered +} // namespace boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_Key_ +|`Key` must be https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^] into the container +https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入] 到容器中的要求,且需满足从容器中 https://en.cppreference.com/w/cpp/named_req/Erasable[可擦除] 的要求。 + +|_Hash_ +|A unary function object type that acts a hash function for a `Key`. It takes a single argument of type `Key` and returns a value of type `std::size_t`. + +|_Pred_ +|A binary function object that induces an equivalence relation on values of type `Key`. It takes two arguments of type `Key` and returns a value of type `bool`. + +|_Allocator_ +|An allocator whose value type is the same as the table's value type. +`std::allocator_traits::pointer` 与 `std::allocator_traits::const_pointer` 必须分别可与 `value_type*` 和 `const value_type*` 相互转换。 + +|=== + +该表的元素节点存储于内部**桶数组**中。节点会根据其元素的哈希值插入到对应的桶内;若该桶已被占用(即发生**哈希冲突**),则使用原位置附近的可用桶进行存储。 + +通过调用 `insert`/`emplace`,或执行 `rehash`/`reserve` 操作,可自动扩容桶数组的大小。容器的**负载因子**(元素数量除以桶数量)永远不会大于 `max_load_factor()`,仅在容器尺寸极小时,实现可能允许负载因子略高于该值。 + +若 `link:../../../../../container_hash/doc/html/hash.html#ref_hash_is_avalanchinghash[hash_is_avalanching]::value` 为 `true`,则直接使用原哈希函数;否则会添加一个位混合后置处理阶段,以额外计算开销为代价提升哈希质量。 + +--- + +=== 并发要求与保证 + +要求对同一 `Hash` 或 `Pred` 常量实例并发调用 `operator()` 时不得引入数据竞争。对于 `Alloc` (即 `Allocator` 或其重绑定后的任意分配器类型),在同一实例 `al` 上并发调用以下操作时不得引入数据竞争: + +* 从 `al` 复制构造重新绑定的分配器 +* `std::allocator++_++traits++<++Alloc++>++::allocate` +* `std::allocator++_++traits++<++Alloc++>++::deallocate` +* `std::allocator++_++traits++<++Alloc++>++::construct` +* `std::allocator++_++traits++<++Alloc++>++::destroy` + +通常而言,若 `Hash` 、 `Pred` 和 `Allocator` 这些类型不包含状态,或其操作仅涉及对内部数据成员的常量访问,即可满足上述要求。 + +除析构操作外,对同一 `concurrent++_++node++_++set` 实例并发调用任何操作均不会引入数据竞争——即这些操作是线程安全的。 + +若某个操作 *op* 被显式指定为__阻塞于__ `x` (其中 `x` 为 `boost::concurrent++_++node++_++set` 实例),则先前对 `x` 的阻塞操作将与 *op* 同步。因此,在多线程场景中,对同一 `concurrent++_++node++_++set` 的阻塞操作将按顺序执行。 + +若某个操作仅在触发内部重哈希时才会阻塞于 _`x`_,则称该操作__阻塞于 _`x`_ 的重哈希过程__。 + +当由 `boost::concurrent++_++node++_++set` 内部执行时,用户提供的访问函数对传递的元素执行以下操作不会引入数据竞争: + +* 对元素的读取访问。 +* 对元素的非可变修改。 +* 对元素的可变修改: +** 在容器接受两个访问函数的操作中,此条件始终适用于第一个访问函数。 ** 在名称不包含 `cvisit` 的非常量容器函数中,此条件适用于最后一个(或唯一一个)访问函数。 + +任何插入或修改元素 `e` 的 `boost::concurrent++_++node++_++set` 操作均与在 `e` 上内部调用的访问函数同步。 + +由 `boost::concurrent++_++node++_++set` `x` 执行的访问函数不得调用 `x` 上的任何操作;仅当对另一 `boost::concurrent++_++node++_++set` 实例 `y` 的并发未完成操作不直接或间接访问 `x` 时,才允许调用 `y` 上的操作。 + +--- + +=== 配置宏 + +==== `BOOST++_++UNORDERED++_++DISABLE++_++REENTRANCY++_++CHECK` + +在调试版本中(更准确地说,当未定义 link:../../../../../assert/doc/html/assert.html#boost_assert_is_void[`BOOST++_++ASSERT++_++IS++_++VOID`] 时),系统会检测__容器重入__行为(即在访问 `m` 元素的函数内部非法调用 `m` 上的操作),并通过 `BOOST++_++ASSERT++_++MSG` 发出信号。若需关注运行时速度,可通过全局定义此宏来禁用该功能。 + +--- + +==== `BOOST++_++UNORDERED++_++ENABLE++_++STATS` + +全局定义此宏以启用容器的 xref:reference/stats.adoc#stats[统计计算] 功能。请注意,此选项会降低多数操作的总体性能。 + +--- + +=== 类型定义 + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ node_type; +---- + +用于保存提取的表元素的类,建模为 https://en.cppreference.com/w/cpp/container/node_handle[NodeHandle] 。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ insert_return_type; +---- + +内部类模板的特化: + +[source,c++,subs=+quotes] +---- +template +struct _insert_return_type_ // name is exposition only +{ + bool inserted; + NodeType node; +}; +---- + +其中 `NodeType` = `node++_++type` 。 + +--- + +=== 常量 + +```cpp static constexpr size_type bulk_visit_size; ``` + +xref:concurrent_node_set_bulk_visit[批量遍历]操作内部使用的块大小。 + +=== 构造函数 + +==== 默认构造函数 +```c++ concurrent_node_set(); ``` + +构造一个空表,使用 `hasher()` 作为哈希函数,`key_equal()` 作为键相等性谓词,`allocator_type()` 作为分配器。 + +[horizontal] +后置条件:;; `size() == 0` +要求:;; 若使用默认参数,`hasher`、`key_equal` 和 `allocator_type` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 桶数构造函数 +```c++ explicit concurrent_node_set(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); ``` + +构造一个包含至少 `n` 个桶的空表,使用 `hf` 作为哈希函数,`eql` 作为键相等性谓词,`a` 作为分配器。 + +[horizontal] +后置条件:;; `size() == 0` +要求:;; 若使用默认参数,`hasher`、`key_equal` 和 `allocator_type` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + concurrent_node_set(InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `eql` 作为键相等性谓词、 `a` 作为分配器,并将 `++[++f, l)` 范围内的元素插入其中。 + +[horizontal] +要求;; 若使用默认值,则 `hasher` 、 `key++_++equal` 和 `allocator++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 复制构造函数 +```c++ concurrent_node_set(concurrent_node_set const& other); ``` + +复制构造函数。复制容器内的元素、哈希函数、谓词以及分配器。 + +若 `Allocator::select_on_container_copy_construction` 存在且签名正确,则分配器将由其返回值构造。 + +[horizontal] +要求:;; `value_type` 是可复制构造的 +并发特性:;; 阻塞 `other` + +--- + +==== 移动构造函数 +```c++ concurrent_node_set(concurrent_node_set&& other); ``` + +移动构造函数。`other` 的内部桶数组会直接转移到新容器中。哈希函数、谓词和分配器均从 `other` 移动构造而来。若启用了统计功能,会从 `other` 转移内部统计信息,并调用 `other.reset_stats()`。 + +[horizontal] +并发特性:;; 阻塞 `other` + +--- + +==== 带分配器的迭代器范围构造函数 +```c++ template concurrent_node_set(InputIterator f, InputIterator l, const allocator_type& a); ``` + +构造一个以 `a` 作为分配器的空容器,使用默认的哈希函数与键相等性谓词,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:;; `hasher`、`key_equal` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 分配器构造函数 +```c++ explicit concurrent_node_set(Allocator const& a); ``` + +构造一个空容器,使用分配器 `a`。 + +--- + +==== 带分配器的复制构造函数 +```c++ concurrent_node_set(concurrent_node_set const& other, Allocator const& a); ``` + +构造一个容器,复制 `other` 中的元素、哈希函数和谓词,但使用分配器 `a`。 + +[horizontal] +并发特性:;; 阻塞 `other` + +--- + +==== 带分配器的移动构造函数 +```c++ concurrent_node_set(concurrent_node_set&& other, Allocator const& a); ``` + +若 `a == other.get_allocator()`,则 `other` 的元素会直接转移到新容器;否则元素从 `other` 移动构造而来。 +哈希函数与谓词从 `other` 移动构造,分配器从 `a` 复制构造。 +若启用了统计功能:仅当 `a == other.get_allocator()` 时,从 `other` 转移内部统计信息;**始终调用** `other.reset_stats()`。 + +[horizontal] +并发特性:;; 阻塞 `other` + +--- + +==== 从 unordered++_++node++_++set 的移动构造函数 + +```c++ concurrent_node_set(unordered_node_set&& other); ``` + +从xref:#unordered_node_set[`unordered_node_set`]执行移动构造。`other`的内部桶数组直接转移至新容器。哈希函数、谓词及分配器均从`other`移动构造。若启用xref:concurrent_node_set_boost_unordered_enable_stats[统计功能],则从`other`转移内部统计信息,并调用`other.reset_stats()`。 + +[horizontal] +复杂度:;; O(`bucket_count()`) + +--- + +==== 初始化列表构造函数 +[source,c++,subs="+quotes"] +---- +concurrent_node_set(std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `eql` 作为键相等性谓词、 `a` 作为分配器,并 `il` 中的元素插入其中。 + +[horizontal] +要求;; 若使用默认值,则 `hasher` 、 `key++_++equal` 和 `allocator++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带分配器的桶数构造函数 +```c++ concurrent_node_set(size_type n, allocator_type const& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、默认的键相等谓词,并以 `a` 作为分配器。 + +[horizontal] +后置条件:;; `size() == 0` +要求:;; `hasher` 和 `key_equal` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 带哈希函数和分配器的桶数构造函数 +```c++ concurrent_node_set(size_type n, hasher const& hf, allocator_type const& a); ``` + +构造一个至少拥有 `n` 个桶的空容器,使用 `hf` 作为哈希函数、默认的键相等性谓词,并以 `a` 作为分配器。 + +[horizontal] +后置条件:;; `size() == 0` +要求:;; `key_equal` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 带桶数和分配器的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + concurrent_node_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 作为分配器以及默认的哈希函数和键相等性谓词,并将 `++[++f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:;; `hasher`、`key_equal` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 带桶数和哈希函数的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- + template + concurrent_node_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `a` 作为分配器以及默认的键相等性谓词,并将 `++[++f, l)` 范围内的元素插入其中。 + +[horizontal] +要求;; `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带分配器的初始化列表构造函数 + +```c++ concurrent_node_set(std::initializer_list il, const allocator_type& a); ``` + +构造一个使用分配器`a`、默认哈希函数和键相等性谓词的空容器,并将`il`中的元素插入其中。 + +[horizontal] +要求:;; `hasher` 和 `key_equal` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 带桶数和分配器的初始化列表构造函数 + +```c++ concurrent_node_set(std::initializer_list il, size_type n, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用分配器 `a`、默认哈希函数和键相等性谓词,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:;; `hasher` 和 `key_equal` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 带桶数、哈希函数和分配器的初始化列表构造函数 + +```c++ concurrent_node_set(std::initializer_list il, size_type n, const hasher& hf, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、`a` 作为分配器以及默认的键相等性谓词,并将 `il` 中的元素插入其中。 + +[horizontal] +要求;; `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +=== 析构函数 + +```c++ ~concurrent_node_set(); ``` + +[horizontal] +注意:;; 析构函数会作用于所有元素,且所有内存都会被释放 + +--- + +=== 赋值操作 + +==== 复制赋值 + +```c++ concurrent_node_set& operator=(concurrent_node_set const& other); ``` + +赋值运算符。销毁先前存在的元素,从`other`复制赋值哈希函数和谓词;若`Alloc::propagate_on_container_copy_assignment`存在且`Alloc::propagate_on_container_copy_assignment::value`为`true`,则从`other`复制赋值分配器;最后插入`other`元素的副本。 + +[horizontal] +要求:;; `value_type` 必须满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^] +并发:;; 阻塞于 `*this` 和 `other`。 + +--- + +==== 移动赋值 +```c++ concurrent_node_set& operator=(concurrent_node_set&& other) noexcept(boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_move_assignment::value); ``` +移动赋值运算符。销毁先前存在的元素,交换other的哈希函数与谓词;若Alloc::propagate_on_container_move_assignment存在且Alloc::propagate_on_container_move_assignment::value为true,则移动赋值other的分配器。若此时分配器与other.get_allocator()相等,直接将other的内部桶数组转移至*this;否则插入other元素的移动构造副本。若启用 xref:concurrent_node_set_boost_unordered_enable_stats [统计功能],当且仅当最终分配器与other.get_allocator()相等时,从other转移内部统计信息,且始终调用other.reset_stats()。 + +[horizontal] +并发:;; 阻塞于 `*this` 和 `other`。 + +--- + +==== 初始化列表赋值 +```c++ concurrent_node_set& operator=(std::initializer_list il); ``` + +通过初始化列表中的值赋值。所有先前存在的元素都会被销毁。 + +[horizontal] +要求:;; `value_type` 必须满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^] +并发:;; 阻塞于 `*this`。 + +--- + +=== 访问操作 + +==== [c]visit + +```c++ template size_t visit(const key_type& k, F f); template size_t visit(const key_type& k, F f) const; template size_t cvisit(const key_type& k, F f) const; template size_t visit(const K& k, F f); template size_t visit(const K& k, F f) const; template size_t cvisit(const K& k, F f) const; ``` + +若存在键与 `k` 等价的元素 `x`,则使用指向 `x` 的常量引用调用 `f`。 + +[horizontal] +返回值:;; 访问过的元素数量(0 或 1)。 +注意:;; 仅当 `Hash::is_transparent` 与 `Pred::is_transparent` 为合法成员别名时,`template` 重载版本才会参与重载决议。库假定 `Hash` 可同时用于 `K` 与 `Key` 类型,且 `Pred` 是透明的。这支持异构查找,避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 批量访问 + +```c++ template size_t visit(FwdIterator first, FwdIterator last, F f); template size_t visit(FwdIterator first, FwdIterator last, F f) const; template size_t cvisit(FwdIterator first, FwdIterator last, F f) const; ``` + +对范围 ++[++`first`, `last`) 内的每个元素 `k` ,若容器中存在键等价于 `k` 的元素 `x` ,则使用指向 `x` 的常量引用调用 `f` 。 + +尽管功能上等同于对每个键单独调用 `[c]visit`,但批量访问因内部优化通常性能更高。建议 `std::distance(first,last)` 至少达到 `bulk_visit_size` 以获得性能提升;超过该大小后,性能预计不会进一步提升。 + +[horizontal] +要求:;; `FwdIterator` 是传统前向迭代器(C++11 至 C++17),或满足 `std::forward_iterator` 概念(C++20 及更高版本)。对于 `K = std::iterator_traits::value_type`,要么 `K` 是 `key_type`,要么 `Hash::is_transparent` 和 `Pred::is_transparent` 是合法的成员别名。在后一种情况下,库假定 `Hash` 可同时接收 `K` 与 `Key` 类型调用,且 `Pred` 是透明的。这支持异构查找,避免了实例化 `Key` 类型对象的开销。返回值:;; 被访问的元素数量。 + +--- + +==== [c]visit_all + +```c++ template size_t visit_all(F f); template size_t visit_all(F f) const; template size_t cvisit_all(F f) const; ``` + +依次以表中每个元素的常量引用调用 `f`。 + +[horizontal] +返回值:;; 被访问的元素数量。 + +--- + +==== 并行 ++[++c++]++visit++_++all + +```c++ template void visit_all(ExecutionPolicy&& policy, F f); template void visit_all(ExecutionPolicy&& policy, F f) const; template void cvisit_all(ExecutionPolicy&& policy, F f) const; ``` + +以表中每个元素的常量引用调用 `f`。执行过程会根据指定执行策略的语义进行并行化。 + +[horizontal] +抛出异常:;; 根据所使用执行策略的异常处理机制,若 `f` 内部抛出异常,则可能调用 `std::terminate`。 +注意:;; 仅在支持 C++17 并行算法的编译器中可用。 +仅当`std::is_execution_policy_v>` 为 `true` 时,这些重载版本才参与重载决议。 +不允许使用无顺序执行策略。 + +--- + +==== [c]visit_while + +```c++ template bool visit_while(F f); template bool visit_while(F f) const; template bool cvisit_while(F f) const; ``` + +依次以表中每个元素的常量引用调用 `f`,直到 `f` 返回 `false` 或遍历完所有元素。 + +[horizontal] +返回值:;; 当且仅当 `f` 返回过 `false` 时,返回 `false`。 + +--- + +==== 并行 ++[++c++]++visit++_++while + +```c++ template bool visit_while(ExecutionPolicy&& policy, F f); template bool visit_while(ExecutionPolicy&& policy, F f) const; template bool cvisit_while(ExecutionPolicy&& policy, F f) const; ``` + +以表中每个元素的常量引用调用 `f`,直到 `f` 返回 `false` 或遍历完所有元素。执行过程会根据指定执行策略的语义进行并行化。 + +[horizontal] +返回值:;; 当且仅当 `f` 返回过 `false` 时,返回 `false`。 +抛出异常:;; 根据所使用执行策略的异常处理机制,若 `f` 内部抛出异常,则可能调用 `std::terminate`。 +注意:;; 仅在支持 C++17 并行算法的编译器中可用。 +仅当 `std::is_execution_policy_v>` 为 `true` 时,这些重载版本才参与重载决议。 +不允许使用无顺序执行策略。 +并行化意味着执行流程不会在 `f` 返回 `false` 时立即终止,因此 `f` 可能会继续被后续元素调用,且这些调用的返回值同样可能为 `false`。 + +--- + +=== 大小与容量 + +==== 空 + +```c++ [[nodiscard]] bool empty() const noexcept; ``` + +[horizontal] +返回值:;; `size() == 0` + +--- + +==== 大小 + +```c++ size_type size() const noexcept; ``` + +[horizontal] +返回值:;; 哈希表中的元素数量。 + +[horizontal] +注意:;; 当存在并发插入操作时,返回的值可能无法精确反映函数执行后哈希表的真实大小。 + +--- + +==== max_size + +```c++ size_type max_size() const noexcept; ``` + +[horizontal] +返回值:;; 哈希表支持的最大元素数量。 + +--- + +=== 修改器 + +==== 原地构造 +```c++ template bool emplace(Args&&... args); ``` + +当且仅当哈希表中不存在键等价的元素时,才使用参数 `args` 构造对象并插入到哈希表中。 + +[horizontal] +要求:;; `value_type` 可由 `args` 构造。 +返回值:;; 若成功插入元素则返回 `true`。 +并发特性:;; 会阻塞当前对象 `*this` 的扩容重哈希操作。 + +--- + +==== 复制插入 +```c++ bool insert(const value_type& obj); ``` + +当且仅当哈希表中不存在键等价的元素时,才将 `obj` 插入到哈希表中。 + +[horizontal] +要求:;; `value_type` 支持拷贝插入。 +返回值:;; 若成功插入元素则返回 `true`。 +并发特性:;; 会阻塞当前对象 `*this` 的扩容重哈希操作。 + +--- + +==== 移动插入 +```c++ bool insert(value_type&& obj); ``` + +当且仅当哈希表中不存在键等价的元素时,才将 `obj` 插入到哈希表中。 + +[horizontal] +要求:;; `value_type` 支持移动插入。 +返回值:;; 若成功插入元素则返回 `true`。 +并发特性:;; 会阻塞当前对象 `*this` 的扩容重哈希操作。 + +--- + +==== 透明插入 +```c++ template bool insert(K&& k); ``` + +当且仅当容器中不存在键等价的元素时,才使用 `std::forward(k)` 构造元素并插入到容器中。 + +[horizontal] +要求:;; `value_type` 可通过 `k` 原位构造。 +返回值:;; 若成功插入元素则返回 `true`。 +并发特性:;; 会阻塞当前对象 `*this` 的扩容重哈希操作。 +注意:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为合法成员别名时,该重载版本参与重载决议。库假定 `Hash` 可同时接收 `K` 与 `Key` 类型调用,且 `Pred` 是透明的。这支持异构查找,避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 迭代器范围插入 +```c++ template size_type insert(InputIterator first, InputIterator last); ``` + +等价于 [listing,subs="+macros,+quotes"] +----- + while(first != last) this->xref:#concurrent_node_set_emplace[emplace](*first++); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== 初始化列表插入 +```c++ size_type insert(std::initializer_list il); ``` + +等价于 [listing,subs="+macros,+quotes"] +----- + this->xref:#concurrent_node_set_insert_iterator_range[insert](il.begin(), il.end()); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== 节点插入 +```c++ insert_return_type insert(node_type&& nh); ``` + +若 `nh` 非空,则当且仅当哈希表中不存在与 `nh.value()` 键等价的元素时,将其关联元素插入哈希表。函数返回时 `nh` 变为空。 + +[horizontal] +返回值:;; 由 `inserted` 和 `node` 构造的 `insert_return_type` 对象。 +* 若 `nh` 为空,则 `inserted` 为 `false` 且 `node` 为空。 +* 若插入操作成功,则 `inserted` 为 true, 且 `node` 为空。 +* 若插入操作失败,则 `inserted` 为 false ,且 `node` 保留 `nh` 的原值。 +若 `nh` 非空,则当且仅当容器中不存在与 `nh.value()` 等效的键时,插入其关联的元素。函数返回时 `nh` 为空。 + +--- + +==== emplace_or_[c]visit +```c++ template bool emplace_or_visit(Args&&... args, F&& f); template bool emplace_or_cvisit(Args&&... args, F&& f); ``` + +若哈希表中不存在键等价的元素,则使用参数 `args` 构造对象并插入表中;否则,以该等价元素的常量引用为参数调用函数 `f`。 + +[horizontal] +要求:;; `value_type` 可由 `args` 构造。 +返回值:;; 若成功插入元素则返回 `true`。 +并发特性:;; 会阻塞当前对象 `*this` 的扩容重哈希操作。 +注意:;; 该接口仅为说明性用法,因为 C++ 不允许在可变参数包之后声明参数 `f`。 + +--- + +==== 复制 insert++_++or++_[++c++]++visit +```c++ template bool insert_or_visit(const value_type& obj, F f); template bool insert_or_cvisit(const value_type& obj, F f); ``` + +当且仅当哈希表中不存在键等价的元素时,将 `obj` 插入表中;否则,以该等价元素的常量引用为参数调用函数 `f`。 + +[horizontal] +要求:;; `value_type` 支持拷贝插入。 +返回值:;; 若成功插入元素则返回 `true`。 +并发特性:;; 会阻塞当前对象 `*this` 的扩容重哈希操作。 + +--- + +==== 移动 insert++_++or++_[++c++]++visit +```c++ template bool insert_or_visit(value_type&& obj, F f); template bool insert_or_cvisit(value_type&& obj, F f); ``` + +当且仅当哈希表中不存在键等价的元素时,将 `obj` 插入表中;否则,以该等价元素的常量引用为参数调用函数 `f`。 + +[horizontal] +要求:;; `value_type` 支持移动插入。 +返回值:;; 若成功插入元素则返回 `true`。 +并发特性:;; 会阻塞当前对象 `*this` 的扩容重哈希操作。 + +--- + +==== 透明 insert++_++or++_[++c++]++visit +```c++ template bool insert_or_visit(K&& k, F f); template bool insert_or_cvisit(K&& k, F f); ``` + +当且仅当容器中不存在键等价的元素时,使用 `std::forward(k)` 构造元素并插入容器;否则,以该等价元素的常量引用为参数调用函数 `f`。 + +[horizontal] +要求:;; `value_type` 可从 `k` 原位构造。 +返回值:;; 若成功插入元素则返回 `true`。 +并发特性:;; 会阻塞当前对象 `*this` 的扩容重哈希操作。 +注意:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为合法成员别名时,该重载版本参与重载决议。库假定 `Hash` 可同时接收 `K` 与 `Key` 类型调用,且 `Pred` 是透明的。这支持异构查找,避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 迭代器范围插入或访问 +```c++ template size_type insert_or_visit(InputIterator first, InputIterator last, F f); template size_type insert_or_cvisit(InputIterator first, InputIterator last, F f); ``` + +等价于 [listing,subs="+macros,+quotes"] +----- + while(first != last) this->xref:#concurrent_node_set_emplace_or_cvisit[emplace_or_[c\]visit](*first++, f); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== 初始化列表插入或访问 +```c++ template size_type insert_or_visit(std::initializer_list il, F f); template size_type insert_or_cvisit(std::initializer_list il, F f); ``` + +等价于 [listing,subs="+macros,+quotes"] +----- + this->xref:#concurrent_node_set_insert_iterator_range_or_visit[insert_or_[c\]visit](il.begin(), il.end(), std::ref(f)); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== 节点插入或访问 +```c++ template insert_return_type insert_or_visit(node_type&& nh, F f); template insert_return_type insert_or_cvisit(node_type&& nh, F f); ``` + +若 `nh` 为空,则不执行任何操作。否则,当且仅当哈希表中不存在与 `nh.value()` 键等价的元素时,将其关联元素插入表中;否则,以该等价元素的常量引用为参数调用函数 `f`。 + +[horizontal] +返回值:;; 由 `inserted` 和 `node` 构造的 `insert_return_type` 对象。 +* 若 `nh` 为空,则 `inserted` 为 `false` 且 `node` 为空。 +* 若插入操作成功,则 `inserted` 为 true, 且 `node` 为空。 +* 若插入操作失败,则 `inserted` 为 false ,且 `node` 保留 `nh` 的原值。 +若 `nh` 为空,则不执行任何操作;否则,当且仅当容器中不存在与 `nh.value()` 等效的键时,插入其关联的元素;否则,使用指向等效元素的常量引用调用 `f` 。 + +--- + +==== emplace_and_[c]visit +```c++ template bool emplace_or_visit(Args&&... args, F1&& f1, F2&& f2); template bool emplace_or_cvisit(Args&&... args, F1&& f1, F2&& f2); ``` + +若哈希表中不存在键等价的元素,则使用参数 `args` 构造对象并插入表中,随后以新建元素的常量引用为参数调用函数 `f1`;否则,以该等价元素的常量引用为参数调用函数 `f2`。 + +[horizontal] +要求:;; `value_type` 可由 `args` 构造。 +返回值:;; 若成功插入元素则返回 `true`。 +并发特性:;; 会阻塞当前对象的重哈希操作。 +注意:;; 该接口仅为说明性用法,因为 C++ 不允许在可变参数包之后声明 `f1` 和 `f2` 参数。 + +--- + +==== 复制 insert++_++and++_[++c++]++visit +```c++ template bool insert_and_visit(const value_type& obj, F1 f1, F2 f2); template bool insert_and_cvisit(const value_type& obj, F1 f2, F2 f2); ``` + +当且仅当哈希表中不存在键等价的元素时,将 `obj` 插入表中,随后以新建元素的常量引用为参数调用函数 `f1`;否则,以该等价元素的常量引用为参数调用函数 `f`。 + +[horizontal] +要求:;; `value_type` 支持拷贝插入。 +返回值:;; 若成功插入元素则返回 `true`。 +并发特性:;; 会阻塞当前对象 `*this` 的扩容重哈希操作。 + +--- + +==== 移动 insert++_++and++_[++c++]++visit +```c++ template bool insert_and_visit(value_type&& obj, F1 f1, F2 f2); template bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2); ``` + +当且仅当哈希表中不存在键等价的元素时,将 `obj` 插入表中,随后以新建元素的常量引用为参数调用函数 `f1`;否则,以该等价元素的常量引用为参数调用函数 `f2`。 +要求:`value_type` 支持拷贝插入。 +返回值:若成功插入元素则返回 `true`。 +并发特性:会阻塞当前对象的重哈希操作。 + +[horizontal] +要求:;; `value_type` 支持移动插入。 +返回值:;; 若成功插入元素则返回 `true`。 +并发特性:;; 会阻塞当前对象 `*this` 的扩容重哈希操作。 + +--- + +==== 透明 insert++_++and++_[++c++]++visit(透明插入并 ++[++c++]++ 访问) +```c++ template bool insert_and_visit(K&& k, F1 f1, F2 f2); template bool insert_and_cvisit(K&& k, F1 f1, F2 f2); ``` + +当且仅当容器中不存在键等价的元素时,通过`std::forward(k)`构造元素并插入容器,随后使用新建元素的常量引用调用`f1`。若存在等价键元素,则使用该元素的常量引用调用`f2`。 + +[horizontal] +要求:;; `value_type` 可从 `k` 原位构造。 +返回值:;; 若成功插入元素则返回 `true`。 +并发特性:;; 会阻塞当前对象 `*this` 的扩容重哈希操作。 +注意:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为合法成员别名时,该重载版本参与重载决议。库假定 `Hash` 可同时接收 `K` 与 `Key` 类型调用,且 `Pred` 是透明的。这支持异构查找,避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 迭代器范围插入并访问 +```c++ template size_type insert_and_visit(InputIterator first, InputIterator last, F1 f1, F2 f2); template size_type insert_and_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2); ``` + +等价于 [listing,subs="+macros,+quotes"] +----- + while(first != last) this->xref:#concurrent_node_set_emplace_and_cvisit[emplace_and_[c\]visit](*first++, f1, f2); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== 初始化列表插入并访问 +```c++ template size_type insert_and_visit(std::initializer_list il, F1 f1, F2 f2); template size_type insert_and_cvisit(std::initializer_list il, F1 f1, F2 f2); ``` + +等价于 [listing,subs="+macros,+quotes"] +----- + this->xref:#concurrent_node_set_insert_iterator_range_and_visit[insert_and_[c\]visit](il.begin(), il.end(), std::ref(f1), std::ref(f2)); +----- + +[horizontal] +返回值:;; 成功插入的元素数量。 + +--- + +==== 节点插入并访问 +```c++ template insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2); template insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2); ``` + +若 `nh` 为空,则不执行任何操作。否则,当且仅当表中不存在与 `nh.value()` 键等价的元素时,将关联元素插入表中,随后以新插入元素的常量引用调用 `f1`;否则,以等价元素的常量引用调用 `f2`。 + +[horizontal] +返回值:;; 由 `inserted` 和 `node` 构造的 `insert_return_type` 对象。 +* 若 `nh` 为空,则 `inserted` 为 `false` 且 `node` 为空。 +* 若插入操作成功,则 `inserted` 为 true, 且 `node` 为空。 +* 若插入操作失败,则 `inserted` 为 false ,且 `node` 保留 `nh` 的原值。 +若 `nh` 为空,则不执行任何操作;否则,当且仅当容器中不存在与 `nh.value()` 等效的键时,插入其关联的元素,并使用指向新插入元素的常量引用调用 `f1` ;否则,使用指向等效元素的常量引用调用 `f2` 。 + +--- + +==== 擦除 +```c++ size_type erase(const key_type& k); template size_type erase(const K& k); ``` + +删除键与 `k` 等价的元素(若该元素存在)。 + +[horizontal] +返回值:;; 删除的元素数量(0 或 1)。 +异常:;; 仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出异常。 +注意:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为合法成员类型别名时,`template` 重载才参与重载决议。标准库假定 `Hash` 可同时接收 `K` 与 `Key` 类型参数调用,且 `Pred` 是透明的。该机制支持异构查找,避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 通过键进行条件擦除 +```c++ template size_type erase_if(const key_type& k, F f); template size_type erase_if(const K& k, F f); ``` + +若键与 `k` 等价的元素 `x` 存在且 `f(x)` 为 `true`,则删除该元素。 + +[horizontal] +返回值:;; 删除的元素数量(0 或 1)。 +异常:;; 仅当 `hasher`、`key_equal` 或 `f` 抛出异常时才会抛出异常。 +注意:;; 仅当 `std::is_execution_policy_v>` 为 `false` 时,`template` 重载才参与重载决议。 +注意:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为合法成员类型别名时,`template` 重载才参与重载决议。标准库假定 `Hash` 可同时接收 `K` 与 `Key` 类型参数调用,且 `Pred` 是透明的。该机制支持异构查找,避免了实例化 `Key` 类型对象的开销。 + +--- + +==== erase_if +```c++ template size_type erase_if(F f); ``` + +依次以表中每个元素的引用调用 `f`,并删除所有 `f` 返回 `true` 的元素。 + +[horizontal] +返回值:;; 删除的元素数量。 +异常:;; 仅当 `f` 抛出异常时才会抛出异常。 + +--- + +==== 并行条件擦除 +```c++ template void erase_if(ExecutionPolicy&& policy, F f); ``` + +以表中每个元素的引用调用 `f`,并删除所有 `f` 返回 `true` 的元素。执行过程将根据指定执行策略的语义进行并行化处理。 + +[horizontal] +异常:;; 依据所使用执行策略的异常处理机制,若 `f` 内部抛出异常,则可能调用 `std::terminate`。 +注意:;; 仅在支持 C++17 并行算法的编译器中可用。 +注意:;; 仅当 `std::is_execution_policy_v>` 为 `true` 时,该重载才参与重载决议。 +注意:;; 不允许使用无序执行策略。 + +--- + +==== 交换 +```c++ void swap(concurrent_node_set& other) noexcept(boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_swap::value); ``` + +将当前表与参数对象的内容进行交换。 + +若 `Allocator::propagate_on_container_swap` 已声明且 `Allocator::propagate_on_container_swap::value` 为 `true`,则交换两个表的分配器。否则,使用不相等的分配器进行交换会导致未定义行为。 + +[horizontal] +异常:;; 除非 `key_equal` 或 `hasher` 在交换时抛出异常,否则不抛出任何异常。 +并发:;; 阻塞 `*this` 和 `other`。 + +--- + +==== extract +```c++ node_type extract(const key_type& k); template node_type extract(K&& k); ``` + +返回值:;; 提取键与 `k` 等价的元素(若该元素存在)。 + +[horizontal] +返回值:;; 存储提取元素的 `node_type` 对象,若未提取任何元素则为空。 +异常:;; 仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出异常。 +注意:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为合法成员类型别名时,`template` 重载才参与重载决议。标准库假定 `Hash` 可同时接收 `K` 与 `Key` 类型参数调用,且 `Pred` 是透明的。该机制支持异构查找,避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 条件提取 +```c++ template node_type extract_if(const key_type& k, F f); template node_type extract_if(K&& k, F f); ``` + +若键与 `k` 等价的元素 `x` 存在且 `f(x)` 为 `true`,则提取该元素。 + +[horizontal] +返回值:;; 存储被提取元素的 `node_type` 对象,若未提取任何元素则为空。 +异常:;; 仅当 `hasher`、`key_equal` 或 `f` 抛出异常时才会抛出异常。 +注意:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为合法成员类型别名时,`template` 重载才参与重载决议。标准库假定 `Hash` 可同时接收 `K` 与 `Key` 类型参数调用,且 `Pred` 是透明的。该机制支持异构查找,避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 清空 +```c++ void clear() noexcept; ``` + +删除表中的所有元素。 + +[horizontal] +后置条件:;; `size() == 0`,`max_load() >= max_load_factor() * bucket_count()` +并发:;; 阻塞 `*this`。 + +--- + +==== 合并 +```c++ template size_type merge(concurrent_node_set& source); template size_type merge(concurrent_node_set&& source); ``` + +将 `source` 中所有键尚未存在于 `*this` 内的元素移动插入,并从 `source` 中删除这些元素。 + +[horizontal] +返回值:;; 插入的元素数量。 +并发:;; 阻塞 `*this` 和 `source`。 + +--- + +=== 观察器 + +==== get_allocator +``` allocator_type get_allocator() const noexcept; ``` + +[horizontal] +返回值:;; 表的分配器。 + +--- + +==== 哈希函数 +``` hasher hash_function() const; ``` + +[horizontal] +返回值:;; 表的哈希函数。 + +--- + +==== key_eq +``` key_equal key_eq() const; ``` + +[horizontal] +返回值:;; 表的键相等性断言。 + +--- + +=== 集合操作 + +==== count +```c++ size_type count(const key_type& k) const; template size_type count(const K& k) const; ``` + +[horizontal] +返回值:;; 键与 `k` 等价的元素数量(0 或 1)。 +注意:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为合法成员类型别名时,`template` 重载才参与重载决议。标准库假定 `Hash` 可同时接收 `K` 与 `Key` 类型参数调用,且 `Pred` 是透明的。该机制支持异构查找,避免了实例化 `Key` 类型对象的开销。 +注意:;; 若存在并发插入操作,返回值可能无法精确反映执行后表的真实状态。 + +--- + +==== 包含 +```c++ bool contains(const key_type& k) const; template bool contains(const K& k) const; ``` + +[horizontal] +返回值:;; 布尔值,表示表中是否存在键等于 `k` 的元素。 +注意:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 为合法成员类型别名时,`template` 重载才参与重载决议。标准库假定 `Hash` 可同时接收 `K` 与 `Key` 类型参数调用,且 `Pred` 是透明的。该机制支持异构查找,避免了实例化 `Key` 类型对象的开销。 +注意:;; 若存在并发插入操作,返回值可能无法精确反映执行后表的真实状态。 + +--- +=== 桶接口 + +==== bucket_count +```c++ size_type bucket_count() const noexcept; ``` + +[horizontal] +返回值:;; 桶数组的大小。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ float load_factor() const noexcept; ``` + +[horizontal] +返回值:;; `static_cast(size())/static_cast(bucket_count())`,若 `bucket_count() == 0` 则返回 `0`。 + +--- + +==== max_load_factor + +```c++ float max_load_factor() const noexcept; ``` + +[horizontal] +返回值:;; 表的最大负载因子。 + +--- + +==== 设置最大负载因子 +```c++ void max_load_factor(float z); ``` + +[horizontal] +效果:;; 不执行任何操作,因为不允许用户修改此参数。保留该函数是为了与 `boost::unordered_set` 保持兼容。 + +--- + + +==== max++_++load(最大负载) + +```c++ size_type max_load() const noexcept; ``` + +[horizontal] +返回值:;; 表在不进行重哈希的情况下可容纳的最大元素数量(假定不会再删除任何元素)。 +注意:;; 构造、重哈希或清空后,表的最大负载量至少为 `max_load_factor() * bucket_count()`。在高负载条件下执行删除操作时,该数值可能会降低。 +注意:;; 若存在并发插入操作,返回值可能无法精确反映执行后表的真实状态。 + +--- + +==== 重哈希 +```c++ void rehash(size_type n); ``` + +效果:;; 必要时调整桶数组大小,确保桶数量至少为 `n`,且负载因子小于等于最大负载因子。适用时,会增大或缩小表关联的桶数量 `bucket_count()`。 + +效果:;; 当 `size() == 0` 时,调用 `rehash(0)` 会释放底层桶数组的内存。 + +[horizontal] +如有必要,将改变桶数组的大小,使其至少包含 `n` 个桶,并确保负载因子小于或等于最大负载因子。此操作将根据情况增加或减少容器的 `bucket++_++count()` 。 + +==== 保留 +```c++ void reserve(size_type n); ``` + +效果:;; 等价于 `a.rehash(ceil(n / a.max_load_factor()))`。 + +效果:;; 与 `rehash` 功能类似,该函数可用于增大或缩小表中的桶数量。 + +[horizontal] +抛出异常:;; 若抛出异常,函数无任何效果(由表的哈希函数或比较函数抛出的异常除外)。 +并发:;; 阻塞 `*this`。 + +--- + +=== 统计信息 + +==== get_stats +```c++ stats get_stats() const; ``` + +[horizontal] +返回值:;; 截至目前,由该表执行的插入与查找操作的统计信息描述。 +注意:;; 仅当**统计计算功能启用**时可用。 + +--- + +==== reset_stats +```c++ void reset_stats() noexcept; ``` + +[horizontal] +效果:;; 将该哈希表所维护的内部统计数据清零。 +注意:;; 仅当xref:reference/stats.adoc#stats[statistics calculation](统计计算功能)被xref:concurrent_node_set_boost_unordered_enable_stats[enabled](启用状态)激活时,本函数才可使用。 + +--- + +=== 推导指引 +如果以下任何一条件为真,则推导指引将不参与重载决议: + +- 该推导指引包含 `InputIterator` 模板参数,且为此参数推导出的类型不符合输入迭代器的要求。 - 该推导指引包含 `Allocator` 模板参数,且为该参数推导出的类型不符合分配器要求。 - 该推导指引包含 `Hash` 模板参数,且为该参数推导出的类型为整型或符合分配器要求。 - 该推导指引包含 `Pred` 模板参数,且为该参数推导出的类型符合分配器要求。 + +推导指引中的 `size++_++type` 参数类型,指向由该推导指引所推导容器类型的 `size++_++type` 成员类型。其默认值与所选构造函数的默认值一致。 + +==== _iter-value-type_ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-value-type__ = + typename std::iterator_traits::value_type; // exposition only +----- + +=== 相等性比较 + +==== operator +```c++ template bool operator==(const concurrent_node_set& x, const concurrent_node_set& y); ``` + +返回值:;; 当且仅当 `x.size() == y.size()`,且对于 `x` 中的每个元素,`y` 中都存在一个拥有相同键、值相等(使用 `operator==` 比较值类型)的元素时,返回 `true`。 + +[horizontal] +并发:;; 对 `x` 和 `y` 进行阻塞操作。 +注意:;; 若两个哈希表的相等性断言不匹配,则行为未定义。 + +--- + +==== operator! +```c++ template bool operator!=(const concurrent_node_set& x, const concurrent_node_set& y); ``` + +返回值:;; 当 `x.size() == y.size()` 且对于 `x` 中的每个元素,`y` 中都存在一个拥有相同键、值相等(使用 `operator==` 比较值类型)的元素时,返回 `false`。 + +[horizontal] +并发:;; 对 `x` 和 `y` 进行阻塞操作。 +注意:;; 若两个哈希表的相等性断言不匹配,则行为未定义。 + +--- + +=== 交换 +```c++ template void swap(concurrent_node_set& x, concurrent_node_set& y) noexcept(noexcept(x.swap(y))); ``` + +等价于 [listing,subs="+macros,+quotes"] +----- +x.xref:#concurrent_node_set_swap[swap](y); +----- + +--- + +=== erase_if +```c++ template typename concurrent_node_set::size_type erase_if(concurrent_node_set& c, Predicate pred); ``` + +等价于 [listing,subs="+macros,+quotes"] +----- +c.xref:#concurrent_node_set_erase_if[erase_if](pred); +----- + +=== 序列化 + +`concurrent++_++node++_++set` 可通过本组件库提供的 API,借助 link:../../../../../serialization/index.html[Boost.Serialization] 进行归档/检索。支持常规归档与 XML 归档两种格式。 + +==== 将 concurrent++_++node++_++set 保存到归档 + +将 `concurrent++_++node++_++set` `x` 的所有元素保存至归档(XML 归档) `ar` 。 + +[horizontal] +要求;; `value++_++type` 必须可序列化(支持 XML 序列化),且需要支持 Boost.Serialization 的 `save++_++construct++_++data` / `load++_++construct++_++data` 协议(该协议自动支持满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求的类型)。 并发性;; 阻塞于 `x` 。 + +--- + +==== 从归档加载 concurrent++_++node++_++set + +删除 `concurrent++_++node++_++set` 容器 `x` 中所有已存在的元素,并从归档(XML 归档) `ar` 中插入原始 `concurrent++_++node++_++set` 容器 `other` 的元素副本,这些副本是从 `ar` 所读取的存储中恢复的。 + +[horizontal] +要求;; `x.key++_++equal()` 需要在功能上等价于 `other.key++_++equal()` 。 并发性;; 阻塞于 `x` 。 diff --git a/doc/modules/ROOT/pages/reference/hash_traits_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/hash_traits_zh_Hans.adoc new file mode 100644 index 0000000..a441129 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/hash_traits_zh_Hans.adoc @@ -0,0 +1,26 @@ +[#hash_traits] +== 哈希特征 + +:idprefix: hash_traits_ + +=== `++<++boost/unordered/hash++_++traits.hpp++>++` 概要 + +[listing,subs="+macros,+quotes"] +----- +#include + +namespace boost { +namespace unordered { + +using boost::hash_is_avalanching; + +} // namespace unordered +} // namespace boost +----- + +[horizontal] +注意;; 此头文件已弃用。请改用定义于 link:../../../../../container_hash/doc/html/hash.html#ref_boostcontainer_hashhash_is_avalanching_hpp[`++<++boost/container++_++hash/hash++_++is++_++avalanching.hpp++>++`] 中的 link:../../../../../container_hash/doc/html/hash.html#ref_hash_is_avalanchinghash[`boost::hash++_++is++_++avalanching`] 。 + +当 `hash++_++is++_++avalanching++<++Hash++>++::value` 为 `true` 时,开放寻址和并发容器会直接使用所提供的哈希函数 `Hash` ;否则它们将实施位混合后处理阶段,以牺牲额外计算成本为代价来提升哈希质量。 + +--- diff --git a/doc/modules/ROOT/pages/reference/header_concurrent_flat_map_fwd_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_concurrent_flat_map_fwd_zh_Hans.adoc new file mode 100644 index 0000000..4d40470 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_concurrent_flat_map_fwd_zh_Hans.adoc @@ -0,0 +1,6 @@ +[#header_concurrent_flat_map_fwd] +== `++<++boost/unordered/concurrent++_++flat++_++map++_++fwd.hpp++>++` 概要 + +:idprefix: header_concurrent_flat_map_fwd_ + +前向声明 xref:reference/header_concurrent_flat_map.adoc[`++<++boost/unordered/concurrent++_++flat++_++map.hpp++>++`] 中的所有定义。 diff --git a/doc/modules/ROOT/pages/reference/header_concurrent_flat_map_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_concurrent_flat_map_zh_Hans.adoc new file mode 100644 index 0000000..0a02a99 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_concurrent_flat_map_zh_Hans.adoc @@ -0,0 +1,57 @@ +[#header_concurrent_flat_map] +== `++<++boost/unordered/concurrent++_++flat++_++map.hpp++>++` 概要 + +:idprefix: header_concurrent_flat_map_ + +定义 xref:reference/concurrent_flat_map.adoc#concurrent_flat_map[`boost::concurrent++_++flat++_++map`] 以及相关的函数与别名模板。 + +[listing,subs="+macros,+quotes"] +----- + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + class xref:reference/concurrent_flat_map.adoc#concurrent_flat_map[concurrent_flat_map]; + + // Equality Comparisons + template + bool xref:reference/concurrent_flat_map.adoc#concurrent_flat_map_operator[operator++==++](const concurrent_flat_map& x, + const concurrent_flat_map& y); + + template + bool xref:reference/concurrent_flat_map.adoc#concurrent_flat_map_operator_2[operator!=](const concurrent_flat_map& x, + const concurrent_flat_map& y); + + // swap + template + void xref:reference/concurrent_flat_map.adoc#concurrent_flat_map_swap_2[swap](concurrent_flat_map& x, + concurrent_flat_map& y) + noexcept(noexcept(x.swap(y))); + + // Erasure + template + typename concurrent_flat_map::size_type + xref:reference/concurrent_flat_map.adoc#concurrent_flat_map_erase_if[erase_if](concurrent_flat_map& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace pmr { + template, + class Pred = std::equal_to> + using concurrent_flat_map = + boost::unordered::concurrent_flat_map>>; + } // namespace pmr + +} // namespace unordered + +using unordered::concurrent_flat_map; + +} // namespace boost +----- diff --git a/doc/modules/ROOT/pages/reference/header_concurrent_flat_set_fwd_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_concurrent_flat_set_fwd_zh_Hans.adoc new file mode 100644 index 0000000..059036a --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_concurrent_flat_set_fwd_zh_Hans.adoc @@ -0,0 +1,6 @@ +[#header_concurrent_flat_set_fwd] +== `++<++boost/unordered/concurrent++_++flat++_++set++_++fwd.hpp++>++` 概要 + +:idprefix: header_concurrent_flat_set_fwd_ + +前向声明 xref:reference/header_concurrent_flat_set.adoc[`++<++boost/unordered/concurrent++_++flat++_++set.hpp++>++`] 中的所有定义。 diff --git a/doc/modules/ROOT/pages/reference/header_concurrent_flat_set_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_concurrent_flat_set_zh_Hans.adoc new file mode 100644 index 0000000..18a5ced --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_concurrent_flat_set_zh_Hans.adoc @@ -0,0 +1,55 @@ +[#header_concurrent_flat_set] +== `++<++boost/unordered/concurrent++_++flat++_++set.hpp++>++` 概要 + +:idprefix: header_concurrent_flat_set_ + +定义 xref:reference/concurrent_flat_set.adoc#concurrent_flat_set[`boost::concurrent++_++flat++_++set`] 及其相关函数和别名模板。 + +[listing,subs="+macros,+quotes"] +----- + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator> + class xref:reference/concurrent_flat_set.adoc#concurrent_flat_set[concurrent_flat_set]; + + // Equality Comparisons + template + bool xref:reference/concurrent_flat_set.adoc#concurrent_flat_set_operator[operator++==++](const concurrent_flat_set& x, + const concurrent_flat_set& y); + + template + bool xref:reference/concurrent_flat_set.adoc#concurrent_flat_set_operator_2[operator!=](const concurrent_flat_set& x, + const concurrent_flat_set& y); + + // swap + template + void xref:reference/concurrent_flat_set.adoc#concurrent_flat_set_swap_2[swap](concurrent_flat_set& x, + concurrent_flat_set& y) + noexcept(noexcept(x.swap(y))); + + // Erasure + template + typename concurrent_flat_set::size_type + xref:reference/concurrent_flat_set.adoc#concurrent_flat_set_erase_if[erase_if](concurrent_flat_set& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace pmr { + template, + class Pred = std::equal_to> + using concurrent_flat_set = + boost::unordered::concurrent_flat_set>; + } // namespace pmr + +} // namespace unordered + +using unordered::concurrent_flat_set; + +} // namespace boost +----- diff --git a/doc/modules/ROOT/pages/reference/header_concurrent_node_map_fwd_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_concurrent_node_map_fwd_zh_Hans.adoc new file mode 100644 index 0000000..a8b0129 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_concurrent_node_map_fwd_zh_Hans.adoc @@ -0,0 +1,6 @@ +[#header_concurrent_node_map_fwd] +== `++<++boost/unordered/concurrent++_++node++_++map++_++fwd.hpp++>++` 概要 + +:idprefix: header_concurrent_node_map_fwd_ + +前向声明 xref:reference/header_concurrent_node_map.adoc[`++<++boost/unordered/concurrent++_++node++_++map.hpp++>++`] 中的所有定义。 diff --git a/doc/modules/ROOT/pages/reference/header_concurrent_node_map_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_concurrent_node_map_zh_Hans.adoc new file mode 100644 index 0000000..1def290 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_concurrent_node_map_zh_Hans.adoc @@ -0,0 +1,57 @@ +[#header_concurrent_node_map] +== `++<++boost/unordered/concurrent++_++node++_++map.hpp++>++` 概要 + +:idprefix: header_concurrent_node_map_ + +定义 xref:reference/concurrent_node_map.adoc#concurrent_node_map[`boost::concurrent++_++node++_++map`] 以及相关函数和别名模板。 + +[listing,subs="+macros,+quotes"] +----- + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + class xref:reference/concurrent_node_map.adoc#concurrent_node_map[concurrent_node_map]; + + // Equality Comparisons + template + bool xref:reference/concurrent_node_map.adoc#concurrent_node_map_operator[operator++==++](const concurrent_node_map& x, + const concurrent_node_map& y); + + template + bool xref:reference/concurrent_node_map.adoc#concurrent_node_map_operator_2[operator!=](const concurrent_node_map& x, + const concurrent_node_map& y); + + // swap + template + void xref:reference/concurrent_node_map.adoc#concurrent_node_map_swap_2[swap](concurrent_node_map& x, + concurrent_node_map& y) + noexcept(noexcept(x.swap(y))); + + // Erasure + template + typename concurrent_node_map::size_type + xref:reference/concurrent_node_map.adoc#concurrent_node_map_erase_if[erase_if](concurrent_node_map& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace pmr { + template, + class Pred = std::equal_to> + using concurrent_node_map = + boost::unordered::concurrent_node_map>>; + } // namespace pmr + +} // namespace unordered + +using unordered::concurrent_node_map; + +} // namespace boost +----- diff --git a/doc/modules/ROOT/pages/reference/header_concurrent_node_set_fwd_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_concurrent_node_set_fwd_zh_Hans.adoc new file mode 100644 index 0000000..2fcfe83 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_concurrent_node_set_fwd_zh_Hans.adoc @@ -0,0 +1,6 @@ +[#header_concurrent_node_set_fwd] +== `++<++boost/unordered/concurrent++_++node++_++set++_++fwd.hpp++>++` 概要 + +:idprefix: header_concurrent_node_set_fwd_ + +前向声明 xref:reference/header_concurrent_node_set.adoc[`++<++boost/unordered/concurrent++_++node++_++set.hpp++>++`] 中的所有定义。 diff --git a/doc/modules/ROOT/pages/reference/header_concurrent_node_set_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_concurrent_node_set_zh_Hans.adoc new file mode 100644 index 0000000..dd66486 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_concurrent_node_set_zh_Hans.adoc @@ -0,0 +1,55 @@ +[#header_concurrent_node_set] +== `++<++boost/unordered/concurrent++_++node++_++set.hpp++>++` 概要 + +:idprefix: header_concurrent_node_set_ + +定义 xref:reference/concurrent_node_set.adoc#concurrent_node_set[`boost::concurrent++_++node++_++set`] 及其相关函数和别名模板。 + +[listing,subs="+macros,+quotes"] +----- + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator> + class xref:reference/concurrent_node_set.adoc#concurrent_node_set[concurrent_node_set]; + + // Equality Comparisons + template + bool xref:reference/concurrent_node_set.adoc#concurrent_node_set_operator[operator++==++](const concurrent_node_set& x, + const concurrent_node_set& y); + + template + bool xref:reference/concurrent_node_set.adoc#concurrent_node_set_operator_2[operator!=](const concurrent_node_set& x, + const concurrent_node_set& y); + + // swap + template + void xref:reference/concurrent_node_set.adoc#concurrent_node_set_swap_2[swap](concurrent_node_set& x, + concurrent_node_set& y) + noexcept(noexcept(x.swap(y))); + + // Erasure + template + typename concurrent_node_set::size_type + xref:reference/concurrent_node_set.adoc#concurrent_node_set_erase_if[erase_if](concurrent_node_set& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace pmr { + template, + class Pred = std::equal_to> + using concurrent_node_set = + boost::unordered::concurrent_node_set>; + } // namespace pmr + +} // namespace unordered + +using unordered::concurrent_node_set; + +} // namespace boost +----- diff --git a/doc/modules/ROOT/pages/reference/header_unordered_flat_map_fwd_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_unordered_flat_map_fwd_zh_Hans.adoc new file mode 100644 index 0000000..07add57 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_unordered_flat_map_fwd_zh_Hans.adoc @@ -0,0 +1,6 @@ +[#header_unordered_flat_map_fwd] +== `++<++boost/unordered/unordered++_++flat++_++map++_++fwd.hpp++>++` 概要 + +:idprefix: header_unordered_flat_map_fwd_ + +前向声明 xref:reference/header_unordered_flat_map.adoc[`++<++boost/unordered/unordered++_++flat++_++map.hpp++>++`] 中定义的所有内容。 diff --git a/doc/modules/ROOT/pages/reference/header_unordered_flat_map_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_unordered_flat_map_zh_Hans.adoc new file mode 100644 index 0000000..df41678 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_unordered_flat_map_zh_Hans.adoc @@ -0,0 +1,57 @@ +[#header_unordered_flat_map] +== `++<++boost/unordered/unordered++_++flat++_++map.hpp++>++` 概要 + +:idprefix: header_unordered_flat_map_ + +定义 xref:reference/unordered_flat_map.adoc#unordered_flat_map[`boost::unordered++_++flat++_++map`] 以及相关函数和别名模板。 + +[listing,subs="+macros,+quotes"] +----- + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + class xref:reference/unordered_flat_map.adoc#unordered_flat_map[unordered_flat_map]; + + // Equality Comparisons + template + bool xref:reference/unordered_flat_map.adoc#unordered_flat_map_operator_2[operator++==++](const unordered_flat_map& x, + const unordered_flat_map& y); + + template + bool xref:reference/unordered_flat_map.adoc#unordered_flat_map_operator_3[operator!=](const unordered_flat_map& x, + const unordered_flat_map& y); + + // swap + template + void xref:reference/unordered_flat_map.adoc#unordered_flat_map_swap_2[swap](unordered_flat_map& x, + unordered_flat_map& y) + noexcept(noexcept(x.swap(y))); + + // Erasure + template + typename unordered_flat_map::size_type + xref:reference/unordered_flat_map.adoc#unordered_flat_map_erase_if[erase_if](unordered_flat_map& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace pmr { + template, + class Pred = std::equal_to> + using unordered_flat_map = + boost::unordered::unordered_flat_map>>; + } // namespace pmr + +} // namespace unordered + +using unordered::unordered_flat_map; + +} // namespace boost +----- diff --git a/doc/modules/ROOT/pages/reference/header_unordered_flat_set_fwd_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_unordered_flat_set_fwd_zh_Hans.adoc new file mode 100644 index 0000000..711be2d --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_unordered_flat_set_fwd_zh_Hans.adoc @@ -0,0 +1,6 @@ +[#header_unordered_flat_set_fwd] +== `++<++boost/unordered/unordered++_++flat++_++set++_++fwd.hpp++>++` 概要 + +:idprefix: header_unordered_flat_set_fwd_ + +前向声明 xref:reference/header_unordered_flat_set.adoc[`++<++boost/unordered/unordered++_++flat++_++set.hpp++>++`] 中的所有定义。 diff --git a/doc/modules/ROOT/pages/reference/header_unordered_flat_set_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_unordered_flat_set_zh_Hans.adoc new file mode 100644 index 0000000..30d0f91 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_unordered_flat_set_zh_Hans.adoc @@ -0,0 +1,55 @@ +[#header_unordered_flat_set] +== `++<++boost/unordered/unordered++_++flat++_++set.hpp++>++` 概要 + +:idprefix: header_unordered_flat_set_ + +定义 xref:reference/unordered_flat_set.adoc#unordered_flat_set[`boost::unordered++_++flat++_++set`] 以及相关的函数和别名模板。 + +[listing,subs="+macros,+quotes"] +----- + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator> + class xref:reference/unordered_flat_set.adoc#unordered_flat_set[unordered_flat_set]; + + // Equality Comparisons + template + bool xref:reference/unordered_flat_set.adoc#unordered_flat_set_operator[operator++==++](const unordered_flat_set& x, + const unordered_flat_set& y); + + template + bool xref:reference/unordered_flat_set.adoc#unordered_flat_set_operator_2[operator!=](const unordered_flat_set& x, + const unordered_flat_set& y); + + // swap + template + void xref:reference/unordered_flat_set.adoc#unordered_flat_set_swap_2[swap](unordered_flat_set& x, + unordered_flat_set& y) + noexcept(noexcept(x.swap(y))); + + // Erasure + template + typename unordered_flat_set::size_type + xref:reference/unordered_flat_set.adoc#unordered_flat_set_erase_if[erase_if](unordered_flat_set& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace pmr { + template, + class Pred = std::equal_to> + using unordered_flat_set = + boost::unordered::unordered_flat_set>; + } // namespace pmr + +} // namespace unordered + +using unordered::unordered_flat_set; + +} // namespace boost +----- diff --git a/doc/modules/ROOT/pages/reference/header_unordered_map_fwd_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_unordered_map_fwd_zh_Hans.adoc new file mode 100644 index 0000000..b1cf8ea --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_unordered_map_fwd_zh_Hans.adoc @@ -0,0 +1,6 @@ +[#header_unordered_map_fwd] +== `` 概要 + +:idprefix: header_unordered_map_fwd_ + +前向声明 xref:reference/header_unordered_map.adoc[``]中的所有定义。 diff --git a/doc/modules/ROOT/pages/reference/header_unordered_map_top_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_unordered_map_top_zh_Hans.adoc new file mode 100644 index 0000000..0f87ad6 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_unordered_map_top_zh_Hans.adoc @@ -0,0 +1,9 @@ +[#header_unordered_map_fwd_top] +== `++<++boost/unordered++_++map.hpp++>++` 概要 + +:idprefix: header_unordered_map_top_ + +[listing,subs="+macros,+quotes"] +----- +#include xref:reference/header_unordered_map.adoc[] +----- diff --git a/doc/modules/ROOT/pages/reference/header_unordered_map_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_unordered_map_zh_Hans.adoc new file mode 100644 index 0000000..318cf06 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_unordered_map_zh_Hans.adoc @@ -0,0 +1,93 @@ +[#header_unordered_map] +== `++<++boost/unordered/unordered++_++map.hpp++>++` 概要 + +:idprefix: header_unordered_map_ + +定义 xref:reference/unordered_map.adoc#unordered_map[`boost::unordered++_++map`] 、 xref:reference/unordered_multimap.adoc#unordered_multimap[`boost::unordered++_++multimap`] 以及相关函数和别名模板。 + +[listing,subs="+macros,+quotes"] +----- + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + class xref:reference/unordered_map.adoc#unordered_map[unordered_map]; + + // Equality Comparisons + template + bool xref:reference/unordered_map.adoc#unordered_map_operator_2[operator++==++](const unordered_map& x, + const unordered_map& y); + + template + bool xref:reference/unordered_map.adoc#unordered_map_operator_3[operator!=](const unordered_map& x, + const unordered_map& y); + + // swap + template + void xref:reference/unordered_map.adoc#unordered_map_swap_2[swap](unordered_map& x, + unordered_map& y) + noexcept(noexcept(x.swap(y))); + + // Erasure + template + typename unordered_map::size_type + xref:reference/unordered_map.adoc#unordered_map_erase_if[erase_if](unordered_map& c, Predicate pred); + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + class xref:reference/unordered_multimap.adoc#unordered_multimap[unordered_multimap]; + + // Equality Comparisons + template + bool xref:reference/unordered_multimap.adoc#unordered_multimap_operator[operator++==++](const unordered_multimap& x, + const unordered_multimap& y); + + template + bool xref:reference/unordered_multimap.adoc#unordered_multimap_operator_2[operator!=](const unordered_multimap& x, + const unordered_multimap& y); + + // swap + template + void xref:reference/unordered_multimap.adoc#unordered_multimap_swap_2[swap](unordered_multimap& x, + unordered_multimap& y) + noexcept(noexcept(x.swap(y))); + + // Erasure + template + typename unordered_multimap::size_type + xref:reference/unordered_multimap.adoc#unordered_multimap_erase_if[erase_if](unordered_multimap& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace pmr { + template, + class Pred = std::equal_to> + using unordered_map = + boost::unordered::unordered_map>>; + + template, + class Pred = std::equal_to> + using unordered_multimap = + boost::unordered::unordered_multimap>>; + } // namespace pmr + +} // namespace unordered + +using unordered::unordered_map; +using unordered::unordered_multimap; + +} // namespace boost +----- diff --git a/doc/modules/ROOT/pages/reference/header_unordered_node_map_fwd_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_unordered_node_map_fwd_zh_Hans.adoc new file mode 100644 index 0000000..64a8c15 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_unordered_node_map_fwd_zh_Hans.adoc @@ -0,0 +1,6 @@ +[#header_unordered_node_map_fwd] +== `++<++boost/unordered/unordered++_++node++_++map++_++fwd.hpp++>++` 概要 + +:idprefix: header_unordered_node_map_fwd_ + +前向声明 xref:reference/header_unordered_node_map.adoc[`++<++boost/unordered/unordered++_++node++_++map.hpp++>++`] 中的所有定义。 diff --git a/doc/modules/ROOT/pages/reference/header_unordered_node_map_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_unordered_node_map_zh_Hans.adoc new file mode 100644 index 0000000..9bb5c40 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_unordered_node_map_zh_Hans.adoc @@ -0,0 +1,57 @@ +[#header_unordered_node_map] +== `` 概要 + +:idprefix: header_unordered_node_map_ + +定义 xref:reference/unordered_node_map.adoc#unordered_node_map[boost::unordered_node_map] 及相关函数和别名模板。 + +[listing,subs="+macros,+quotes"] +----- + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + class xref:reference/unordered_node_map.adoc#unordered_node_map[unordered_node_map]; + + // Equality Comparisons + template + bool xref:reference/unordered_node_map.adoc#unordered_node_map_operator_2[operator++==++](const unordered_node_map& x, + const unordered_node_map& y); + + template + bool xref:reference/unordered_node_map.adoc#unordered_node_map_operator_3[operator!=](const unordered_node_map& x, + const unordered_node_map& y); + + // swap + template + void xref:reference/unordered_node_map.adoc#unordered_node_map_swap_2[swap](unordered_node_map& x, + unordered_node_map& y) + noexcept(noexcept(x.swap(y))); + + // Erasure + template + typename unordered_node_map::size_type + xref:reference/unordered_node_map.adoc#unordered_node_map_erase_if[erase_if](unordered_node_map& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace pmr { + template, + class Pred = std::equal_to> + using unordered_node_map = + boost::unordered::unordered_node_map>>; + } // namespace pmr + +} // namespace unordered + +using unordered::unordered_node_map; + +} // namespace boost +----- diff --git a/doc/modules/ROOT/pages/reference/header_unordered_node_set_fwd_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_unordered_node_set_fwd_zh_Hans.adoc new file mode 100644 index 0000000..5467c1b --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_unordered_node_set_fwd_zh_Hans.adoc @@ -0,0 +1,6 @@ +[#header_unordered_node_set_fwd] +== `++<++boost/unordered/unordered++_++node++_++set++_++fwd.hpp++>++` 概要 + +:idprefix: header_unordered_node_set_fwd_ + +前向声明 xref:reference/header_unordered_node_set.adoc[`++<++boost/unordered/unordered++_++node++_++set.hpp++>++`] 中的所有定义。 diff --git a/doc/modules/ROOT/pages/reference/header_unordered_node_set_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_unordered_node_set_zh_Hans.adoc new file mode 100644 index 0000000..f738c95 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_unordered_node_set_zh_Hans.adoc @@ -0,0 +1,55 @@ +[#header_unordered_node_set] +== `++<++boost/unordered/unordered++_++node++_++set.hpp++>++` 概要 + +:idprefix: header_unordered_node_set_ + +定义 xref:reference/unordered_node_set.adoc#unordered_node_set[`boost::unordered++_++node++_++set`] 及其相关函数和别名模板。 + +[listing,subs="+macros,+quotes"] +----- + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator> + class xref:reference/unordered_node_set.adoc#unordered_node_set[unordered_node_set]; + + // Equality Comparisons + template + bool xref:reference/unordered_node_set.adoc#unordered_node_set_operator[operator++==++](const unordered_node_set& x, + const unordered_node_set& y); + + template + bool xref:reference/unordered_node_set.adoc#unordered_node_set_operator_2[operator!=](const unordered_node_set& x, + const unordered_node_set& y); + + // swap + template + void xref:reference/unordered_node_set.adoc#unordered_node_set_swap_2[swap](unordered_node_set& x, + unordered_node_set& y) + noexcept(noexcept(x.swap(y))); + + // Erasure + template + typename unordered_node_set::size_type + xref:reference/unordered_node_set.adoc#unordered_node_set_erase_if[erase_if](unordered_node_set& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace pmr { + template, + class Pred = std::equal_to> + using unordered_node_set = + boost::unordered::unordered_node_set>; + } // namespace pmr + +} // namespace unordered + +using unordered::unordered_node_set; + +} // namespace boost +----- diff --git a/doc/modules/ROOT/pages/reference/header_unordered_set_fwd_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_unordered_set_fwd_zh_Hans.adoc new file mode 100644 index 0000000..0be97ba --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_unordered_set_fwd_zh_Hans.adoc @@ -0,0 +1,6 @@ +[#header_unordered_set_fwd] +== `++<++boost/unordered/unordered++_++set++_++fwd.hpp++>++` 概要 + +:idprefix: header_unordered_set_fwd_ + +前向声明 xref:reference/header_unordered_set.adoc[`++<++boost/unordered/unordered++_++set.hpp++>++`] 中的所有定义。 diff --git a/doc/modules/ROOT/pages/reference/header_unordered_set_top_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_unordered_set_top_zh_Hans.adoc new file mode 100644 index 0000000..f64dd68 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_unordered_set_top_zh_Hans.adoc @@ -0,0 +1,9 @@ +[#header_unordered_set_fwd_top] +== `++<++boost/unordered++_++set.hpp++>++` 概要 + +:idprefix: header_unordered_set_top_ + +[listing,subs="+macros,+quotes"] +----- +#include xref:reference/header_unordered_set.adoc[] +----- diff --git a/doc/modules/ROOT/pages/reference/header_unordered_set_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/header_unordered_set_zh_Hans.adoc new file mode 100644 index 0000000..048d4f6 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_unordered_set_zh_Hans.adoc @@ -0,0 +1,89 @@ +[#header_unordered_set] +== `++<++boost/unordered/unordered++_++set.hpp++>++` 概要 + +:idprefix: header_unordered_set_ + +定义 xref:reference/unordered_set.adoc#unordered_set[`boost::unordered++_++set`] 、 xref:reference/unordered_multiset.adoc#unordered_multiset[`boost::unordered++_++multiset`] 以及相关的函数和别名模板。 + +[listing,subs="+macros,+quotes"] +----- + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator> + class xref:reference/unordered_set.adoc#unordered_set[unordered_set]; + + // Equality Comparisons + template + bool xref:reference/unordered_set.adoc#unordered_set_operator[operator++==++](const unordered_set& x, + const unordered_set& y); + + template + bool xref:reference/unordered_set.adoc#unordered_set_operator_2[operator!=](const unordered_set& x, + const unordered_set& y); + + // swap + template + void xref:reference/unordered_set.adoc#unordered_set_swap_2[swap](unordered_set& x, + unordered_set& y) + noexcept(noexcept(x.swap(y))); + + // Erasure + template + typename unordered_set::size_type + xref:reference/unordered_set.adoc#unordered_set_erase_if[erase_if](unordered_set& c, Predicate pred); + + template, + class Pred = std::equal_to, + class Allocator = std::allocator> + class xref:reference/unordered_multiset.adoc#unordered_multiset[unordered_multiset]; + + // Equality Comparisons + template + bool xref:reference/unordered_multiset.adoc#unordered_multiset_operator[operator++==++](const unordered_multiset& x, + const unordered_multiset& y); + + template + bool xref:reference/unordered_multiset.adoc#unordered_multiset_operator_2[operator!=](const unordered_multiset& x, + const unordered_multiset& y); + + // swap + template + void xref:reference/unordered_multiset.adoc#unordered_multiset_swap_2[swap](unordered_multiset& x, + unordered_multiset& y) + noexcept(noexcept(x.swap(y))); + + // Erasure + template + typename unordered_multiset::size_type + xref:reference/unordered_multiset.adoc#unordered_multiset_erase_if[erase_if](unordered_multiset& c, Predicate pred); + + // Pmr aliases (C++17 and up) + namespace pmr { + template, + class Pred = std::equal_to> + using unordered_set = + boost::unordered::unordered_set>; + + template, + class Pred = std::equal_to> + using unordered_multiset = + boost::unordered::unordered_multiset>; + } // namespace pmr + +} // namespace unordered + +using unordered::unordered_set; +using unordered::unordered_multiset; + +} // namespace boost +----- diff --git a/doc/modules/ROOT/pages/reference/stats_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/stats_zh_Hans.adoc new file mode 100644 index 0000000..15d9ba4 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/stats_zh_Hans.adoc @@ -0,0 +1,62 @@ +[#stats] +== 统计信息 + +:idprefix: stats_ + +开放寻址和并发容器可配置为持续统计受所提供哈希函数质量影响的某些内部操作。 + +=== 概要 + +[listing,subs="+macros,+quotes"] +----- +struct xref:#stats_stats_summary_type[__stats-summary-type__] +{ + double average; + double variance; + double deviation; +}; + +struct xref:#stats_insertion_stats_type[__insertion-stats-type__] +{ + std::size_t count; + xref:#stats_stats_summary_type[__stats-summary-type__] probe_length; +}; + +struct xref:stats_lookup_stats_type[__lookup-stats-type__] +{ + std::size_t count; + xref:#stats_stats_summary_type[__stats-summary-type__] probe_length; + xref:#stats_stats_summary_type[__stats-summary-type__] num_comparisons; +}; + +struct xref:reference/stats.adoc#stats_stats_type[__stats-type__] +{ + xref:#stats_insertion_stats_type[__insertion-stats-type__] insertion; + xref:stats_lookup_stats_type[__lookup-stats-type__] successful_lookup, + unsuccessful_lookup; +}; +----- + +==== _统计摘要类型_ + +提供数值序列的平均值、方差和标准差。 + +==== _插入统计类型_ + +提供容器执行的插入操作次数及相关__探查长度__(每次操作访问的 xref:structures.adoc#structures_open_addressing_containers[桶组] 数量)的统计信息。 + +==== _查找统计类型_ + +对于成功(找到元素)或失败(未找到)查找操作,提供容器执行的操作次数及相关__探查长度__(访问的 xref:structures.adoc#structures_open_addressing_containers[桶组] 数量)和每次操作的元素比较次数的统计信息。 + +==== _stats-type_ + +提供容器执行的插入操作、成功及失败查找操作的统计信息。若提供的哈希函数质量良好,则: + +* 平均探查长度应接近1.0。 +* 对于成功查找,平均元素比较次数应接近 1.0。 +* 对于失败查找,平均元素比较次数应接近 0.0。 + +这些统计信息可用于判断给定的哈希函数是否可被标记为 link:../../../../../container_hash/doc/html/hash.html#ref_hash_is_avalanchinghash[_雪崩效应_] 。 + +--- diff --git a/doc/modules/ROOT/pages/reference/unordered_flat_map_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/unordered_flat_map_zh_Hans.adoc new file mode 100644 index 0000000..54a74d4 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/unordered_flat_map_zh_Hans.adoc @@ -0,0 +1,1175 @@ +[#unordered_flat_map] +== 类模板 unordered++_++flat++_++map + +:idprefix: unordered_flat_map_ + +`boost::unordered++_++flat++_++map` —— 一种开放寻址的无序关联容器,用于将唯一键与另一个值关联。 + +`boost::unordered++_++flat++_++map` 的性能远优于 `boost::unordered++_++map` 或其他 `std::unordered++_++map` 的实现。与基于节点的标准无序关联容器不同, `boost::unordered++_++flat++_++map` 的元素直接存储在桶数组中,且当元素被插入到已被占用的桶时,会将其重定向到原始位置附近的可用桶。这种数据布局类型称为__开放寻址法__。 + +由于采用开放寻址法, `boost::unordered++_++flat++_++map` 的接口在多个方面与 `boost::unordered++_++map` / `std::unordered++_++map` 不同: + +- `value++_++type` 必须支持移动构造。 - 在重哈希的过程中,指针稳定性无法保持。 - `begin()` 不是常数时间复杂度操作。 - 未提供用于桶管理(除 `bucket++_++count` 外)或节点提取/插入的 API。 - 容器的最大负载因子由内部管理,用户无法进行设置。 + +除此之外, `boost::unordered++_++flat++_++map` 基本可完全替代基于节点的标准无序关联容器。 + +=== 概要 + +[listing,subs="+macros,+quotes"] +----- +// #include xref:reference/header_unordered_flat_map.adoc[``] + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + class unordered_flat_map { + public: + // types + using key_type = Key; + using mapped_type = T; + using value_type = std::pair; + using init_type = std::pair< + typename std::remove_const::type, + typename std::remove_const::type + >; + using hasher = Hash; + using key_equal = Pred; + using allocator_type = Allocator; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + using iterator = _implementation-defined_; + using const_iterator = _implementation-defined_; + + using stats = xref:reference/stats.adoc#stats_stats_type[__stats-type__]; // if statistics are xref:unordered_flat_map_boost_unordered_enable_stats[enabled] + + // construct/copy/destroy + xref:#unordered_flat_map_default_constructor[unordered_flat_map](); + explicit xref:#unordered_flat_map_bucket_count_constructor[unordered_flat_map](size_type n, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + template + xref:#unordered_flat_map_iterator_range_constructor[unordered_flat_map](InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#unordered_flat_map_copy_constructor[unordered_flat_map](const unordered_flat_map& other); + xref:#unordered_flat_map_move_constructor[unordered_flat_map](unordered_flat_map&& other); + template + xref:#unordered_flat_map_iterator_range_constructor_with_allocator[unordered_flat_map](InputIterator f, InputIterator l, const allocator_type& a); + explicit xref:#unordered_flat_map_allocator_constructor[unordered_flat_map](const Allocator& a); + xref:#unordered_flat_map_copy_constructor_with_allocator[unordered_flat_map](const unordered_flat_map& other, const Allocator& a); + xref:#unordered_flat_map_move_constructor_with_allocator[unordered_flat_map](unordered_flat_map&& other, const Allocator& a); + xref:#unordered_flat_map_move_constructor_from_concurrent_flat_map[unordered_flat_map](concurrent_flat_map&& other); + xref:#unordered_flat_map_initializer_list_constructor[unordered_flat_map](std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#unordered_flat_map_bucket_count_constructor_with_allocator[unordered_flat_map](size_type n, const allocator_type& a); + xref:#unordered_flat_map_bucket_count_constructor_with_hasher_and_allocator[unordered_flat_map](size_type n, const hasher& hf, const allocator_type& a); + template + xref:#unordered_flat_map_iterator_range_constructor_with_bucket_count_and_allocator[unordered_flat_map](InputIterator f, InputIterator l, size_type n, const allocator_type& a); + template + xref:#unordered_flat_map_iterator_range_constructor_with_bucket_count_and_hasher[unordered_flat_map](InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); + xref:#unordered_flat_map_initializer_list_constructor_with_allocator[unordered_flat_map](std::initializer_list il, const allocator_type& a); + xref:#unordered_flat_map_initializer_list_constructor_with_bucket_count_and_allocator[unordered_flat_map](std::initializer_list il, size_type n, + const allocator_type& a); + xref:#unordered_flat_map_initializer_list_constructor_with_bucket_count_and_hasher_and_allocator[unordered_flat_map](std::initializer_list il, size_type n, const hasher& hf, + const allocator_type& a); + xref:#unordered_flat_map_destructor[~unordered_flat_map](); + unordered_flat_map& xref:#unordered_flat_map_copy_assignment[operator++=++](const unordered_flat_map& other); + unordered_flat_map& xref:#unordered_flat_map_move_assignment[operator++=++](unordered_flat_map&& other) ++noexcept( + (boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_move_assignment::value) && + std::is_same::value);++ + unordered_flat_map& xref:#unordered_flat_map_initializer_list_assignment[operator++=++](std::initializer_list); + allocator_type xref:#unordered_flat_map_get_allocator[get_allocator]() const noexcept; + + // iterators + iterator xref:#unordered_flat_map_begin[begin]() noexcept; + const_iterator xref:#unordered_flat_map_begin[begin]() const noexcept; + iterator xref:#unordered_flat_map_end[end]() noexcept; + const_iterator xref:#unordered_flat_map_end[end]() const noexcept; + const_iterator xref:#unordered_flat_map_cbegin[cbegin]() const noexcept; + const_iterator xref:#unordered_flat_map_cend[cend]() const noexcept; + + // capacity + ++[[nodiscard]]++ bool xref:#unordered_flat_map_empty[empty]() const noexcept; + size_type xref:#unordered_flat_map_size[size]() const noexcept; + size_type xref:#unordered_flat_map_max_size[max_size]() const noexcept; + + // modifiers + template std::pair xref:#unordered_flat_map_emplace[emplace](Args&&... args); + template iterator xref:#unordered_flat_map_emplace_hint[emplace_hint](const_iterator position, Args&&... args); + std::pair xref:#unordered_flat_map_copy_insert[insert](const value_type& obj); + std::pair xref:#unordered_flat_map_copy_insert[insert](const init_type& obj); + std::pair xref:#unordered_flat_map_move_insert[insert](value_type&& obj); + std::pair xref:#unordered_flat_map_move_insert[insert](init_type&& obj); + iterator xref:#unordered_flat_map_copy_insert_with_hint[insert](const_iterator hint, const value_type& obj); + iterator xref:#unordered_flat_map_copy_insert_with_hint[insert](const_iterator hint, const init_type& obj); + iterator xref:#unordered_flat_map_move_insert_with_hint[insert](const_iterator hint, value_type&& obj); + iterator xref:#unordered_flat_map_copy_insert_with_hint[insert](const_iterator hint, init_type&& obj); + template void xref:#unordered_flat_map_insert_iterator_range[insert](InputIterator first, InputIterator last); + void xref:#unordered_flat_map_insert_initializer_list[insert](std::initializer_list); + + template + std::pair xref:#unordered_flat_map_try_emplace[try_emplace](const key_type& k, Args&&... args); + template + std::pair xref:#unordered_flat_map_try_emplace[try_emplace](key_type&& k, Args&&... args); + template + std::pair xref:#unordered_flat_map_try_emplace[try_emplace](K&& k, Args&&... args); + template + iterator xref:#unordered_flat_map_try_emplace_with_hint[try_emplace](const_iterator hint, const key_type& k, Args&&... args); + template + iterator xref:#unordered_flat_map_try_emplace_with_hint[try_emplace](const_iterator hint, key_type&& k, Args&&... args); + template + iterator xref:#unordered_flat_map_try_emplace_with_hint[try_emplace](const_iterator hint, K&& k, Args&&... args); + template + std::pair xref:#unordered_flat_map_insert_or_assign[insert_or_assign](const key_type& k, M&& obj); + template + std::pair xref:#unordered_flat_map_insert_or_assign[insert_or_assign](key_type&& k, M&& obj); + template + std::pair xref:#unordered_flat_map_insert_or_assign[insert_or_assign](K&& k, M&& obj); + template + iterator xref:#unordered_flat_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, const key_type& k, M&& obj); + template + iterator xref:#unordered_flat_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, key_type&& k, M&& obj); + template + iterator xref:#unordered_flat_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, K&& k, M&& obj); + + _convertible-to-iterator_ xref:#unordered_flat_map_erase_by_position[erase](iterator position); + _convertible-to-iterator_ xref:#unordered_flat_map_erase_by_position[erase](const_iterator position); + size_type xref:#unordered_flat_map_erase_by_key[erase](const key_type& k); + template size_type xref:#unordered_flat_map_erase_by_key[erase](K&& k); + iterator xref:#unordered_flat_map_erase_range[erase](const_iterator first, const_iterator last); + void xref:#unordered_flat_map_swap[swap](unordered_flat_map& other) + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_swap::value); + init_type xref:#unordered_flat_map_pull[pull](const_iterator position); + void xref:#unordered_flat_map_clear[clear]() noexcept; + + template + void xref:#unordered_flat_map_merge[merge](unordered_flat_map& source); + template + void xref:#unordered_flat_map_merge[merge](unordered_flat_map&& source); + + // observers + hasher xref:#unordered_flat_map_hash_function[hash_function]() const; + key_equal xref:#unordered_flat_map_key_eq[key_eq]() const; + + // map operations + iterator xref:#unordered_flat_map_find[find](const key_type& k); + const_iterator xref:#unordered_flat_map_find[find](const key_type& k) const; + template + iterator xref:#unordered_flat_map_find[find](const K& k); + template + const_iterator xref:#unordered_flat_map_find[find](const K& k) const; + size_type xref:#unordered_flat_map_count[count](const key_type& k) const; + template + size_type xref:#unordered_flat_map_count[count](const K& k) const; + bool xref:#unordered_flat_map_contains[contains](const key_type& k) const; + template + bool xref:#unordered_flat_map_contains[contains](const K& k) const; + std::pair xref:#unordered_flat_map_equal_range[equal_range](const key_type& k); + std::pair xref:#unordered_flat_map_equal_range[equal_range](const key_type& k) const; + template + std::pair xref:#unordered_flat_map_equal_range[equal_range](const K& k); + template + std::pair xref:#unordered_flat_map_equal_range[equal_range](const K& k) const; + + // element access + mapped_type& xref:#unordered_flat_map_operator[operator[+]+](const key_type& k); + mapped_type& xref:#unordered_flat_map_operator[operator[+]+](key_type&& k); + template mapped_type& xref:#unordered_flat_map_operator[operator[+]+](K&& k); + mapped_type& xref:#unordered_flat_map_at[at](const key_type& k); + const mapped_type& xref:#unordered_flat_map_at[at](const key_type& k) const; + template mapped_type& xref:#unordered_flat_map_at[at](const K& k); + template const mapped_type& xref:#unordered_flat_map_at[at](const K& k) const; + + // bucket interface + size_type xref:#unordered_flat_map_bucket_count[bucket_count]() const noexcept; + + // hash policy + float xref:#unordered_flat_map_load_factor[load_factor]() const noexcept; + float xref:#unordered_flat_map_max_load_factor[max_load_factor]() const noexcept; + void xref:#unordered_flat_map_set_max_load_factor[max_load_factor](float z); + size_type xref:#unordered_flat_map_max_load[max_load]() const noexcept; + void xref:#unordered_flat_map_rehash[rehash](size_type n); + void xref:#unordered_flat_map_reserve[reserve](size_type n); + + // statistics (if xref:unordered_flat_map_boost_unordered_enable_stats[enabled]) + stats xref:#unordered_flat_map_get_stats[get_stats]() const; + void xref:#unordered_flat_map_reset_stats[reset_stats]() noexcept; + }; + + // Deduction Guides + template>, + class Pred = std::equal_to>, + class Allocator = std::allocator>> + unordered_flat_map(InputIterator, InputIterator, typename xref:#unordered_flat_map_deduction_guides[__see below__]::size_type = xref:#unordered_flat_map_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_flat_map, xref:#unordered_flat_map_iter_mapped_type[__iter-mapped-type__], Hash, + Pred, Allocator>; + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + unordered_flat_map(std::initializer_list>, + typename xref:#unordered_flat_map_deduction_guides[__see below__]::size_type = xref:#unordered_flat_map_deduction_guides[__see below__], Hash = Hash(), + Pred = Pred(), Allocator = Allocator()) + -> unordered_flat_map; + + template + unordered_flat_map(InputIterator, InputIterator, typename xref:#unordered_flat_map_deduction_guides[__see below__]::size_type, Allocator) + -> unordered_flat_map, xref:#unordered_flat_map_iter_mapped_type[__iter-mapped-type__], + boost::hash>, + std::equal_to>, Allocator>; + + template + unordered_flat_map(InputIterator, InputIterator, Allocator) + -> unordered_flat_map, xref:#unordered_flat_map_iter_mapped_type[__iter-mapped-type__], + boost::hash>, + std::equal_to>, Allocator>; + + template + unordered_flat_map(InputIterator, InputIterator, typename xref:#unordered_flat_map_deduction_guides[__see below__]::size_type, Hash, + Allocator) + -> unordered_flat_map, xref:#unordered_flat_map_iter_mapped_type[__iter-mapped-type__], Hash, + std::equal_to>, Allocator>; + + template + unordered_flat_map(std::initializer_list>, typename xref:#unordered_flat_map_deduction_guides[__see below__]::size_type, + Allocator) + -> unordered_flat_map, std::equal_to, Allocator>; + + template + unordered_flat_map(std::initializer_list>, Allocator) + -> unordered_flat_map, std::equal_to, Allocator>; + + template + unordered_flat_map(std::initializer_list>, typename xref:#unordered_flat_map_deduction_guides[__see below__]::size_type, + Hash, Allocator) + -> unordered_flat_map, Allocator>; + +} // namespace unordered +} // namespace boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_Key_ +.2+|`Key` and `T` must be https://en.cppreference.com/w/cpp/named_req/MoveConstructible[MoveConstructible^]. +`std::pair` 必须能从任何可转换为它的 `std::pair` 对象满足对容器的 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^] 要求,并且还必须满足对容器的 https://en.cppreference.com/w/cpp/named_req/Erasable[可擦除^] 要求。 + +|_T_ + +|_Hash_ +|A unary function object type that acts a hash function for a `Key`. It takes a single argument of type `Key` and returns a value of type `std::size_t`. + +|_Pred_ +|A binary function object that induces an equivalence relation on values of type `Key`. It takes two arguments of type `Key` and returns a value of type `bool`. + +|_Allocator_ +|An allocator whose value type is the same as the container's value type. +支持使用 https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[花式指针^] 的分配器。 + +|=== + +容器的元素存储在内部的__桶数组__中。元素根据其哈希码被插入到对应的桶中,但如果该桶已被占用(即发生__冲突__),则会使用原始位置附近可用的桶。 + +桶数组的大小可通过调用 `insert` / `emplace` 自动增加,也可通过调用 `rehash` / `reserve` 来调整。容器的__负载因子__(元素数量与桶数量的比值)始终不会超过 `max++_++load++_++factor()` ,但在小规模数据情况下,实现可能允许更高的负载因子。 + +若 link:../../../../../container_hash/doc/html/hash.html#ref_hash_is_avalanchinghash[`hash++_++is++_++avalanching`]`++<++Hash++>++::value` 为 `true` ,则直接使用哈希函数;否则,会添加一个位混合后处理阶段以提高哈希质量,但会牺牲额外的计算成本。 + +--- + +=== 配置宏 + +==== `BOOST++_++UNORDERED++_++ENABLE++_++STATS` + +全局定义此宏,以启用容器的 xref:reference/stats.adoc#stats[统计计算] 功能。请注意,此选项会降低许多操作的总体性能。 + +--- + +=== 类型定义 + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ iterator; +---- + +一种迭代器,其值类型为 `value++_++type` 。 + +迭代器类别至少为前向迭代器。 + +可转换为 `const++_++iterator` 。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ const_iterator; +---- + +一个常量迭代器,其值类型为 `value++_++type` 。 + +迭代器类别至少为前向迭代器。 + +=== 构造函数 + +==== 默认构造函数 +```c++ unordered_flat_map(); ``` + +构造一个空容器,使用 `hasher()` 作为哈希函数、 `key++_++equal()` 作为键相等性谓词、以及 `allocator++_++type()` 作为分配器。 + +[horizontal] +后置条件:`size() == 0` 要求:若使用默认参数,则 `hasher`、`key_equal` 和 `allocator_type` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 桶数构造函数 +```c++ explicit unordered_flat_map(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `eql` 作为键相等性谓词、以及 `a` 作为分配器。 + +[horizontal] +后置条件:`size() == 0` 要求:若使用默认参数,则 `hasher`、`key_equal` 和 `allocator_type` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_flat_map(InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `eql` 作为键相等性谓词、 `a` 作为分配器,并将区间 `++[++f, l)` 中的元素插入其中。 + +[horizontal] +要求;; 若使用默认值,则 `hasher` 、 `key++_++equal` 和 `allocator++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 复制构造函数 +```c++ unordered_flat_map(unordered_flat_map const& other); ``` + +复制构造函数。复制所含元素、哈希函数、谓词和分配器。 + +若 `Allocator::select++_++on++_++container++_++copy++_++construction` 存在且签名正确,则将根据其结果来构造分配器。 + +[horizontal] +要求:`value_type` 需满足可复制构造要求。 + +--- + +==== 移动构造函数 +```c++ unordered_flat_map(unordered_flat_map&& other); ``` + +移动构造函数。`other` 的内部桶数组直接转移到新容器中。哈希函数、谓词和分配器通过移动构造从 `other` 获得。若统计功能已启用(参见 xref:unordered_flat_map_boost_unordered_enable_stats[相关说明]),则转移 `other` 的内部统计信息并调用 `other.reset_stats()`。 + +--- + +==== 带分配器的迭代器范围构造函数 +```c++ template unordered_flat_map(InputIterator f, InputIterator l, const allocator_type& a); ``` + +使用 `a` 作为分配器构造一个空容器,使用默认的哈希函数和键相等谓词,并将 `[f, l)` 中的元素插入其中。 + +[horizontal] +要求:`hasher`、`key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 分配器构造函数 +```c++ explicit unordered_flat_map(Allocator const& a); ``` + +使用分配器 a 构造一个空容器。 + +--- + +==== 带分配器的复制构造函数 +```c++ unordered_flat_map(unordered_flat_map const& other, Allocator const& a); ``` + +构造一个容器,复制 `other` 所包含的元素、哈希函数和谓词,但使用分配器 `a`。 + +--- + +==== 带分配器的移动构造函数 +```c++ unordered_flat_map(unordered_flat_map&& other, Allocator const& a); ``` + +若 `a == other.get_allocator()`,则 `other` 的元素直接转移到新容器中;否则,将通过移动构造从 `other` 的元素创建新元素。哈希函数和谓词通过移动构造从 `other` 获得,分配器则通过复制构造从 `a` 获得。若统计功能已启用(参见 xref:unordered_flat_map_boost_unordered_enable_stats[相关说明]),则仅当 `a == other.get_allocator()` 时转移 `other` 的内部统计信息,且始终会调用 `other.reset_stats()`。 + +--- + +==== 从 concurrent++_++flat++_++map 的移动构造函数 + +```c++ unordered_flat_map(concurrent_flat_map&& other); ``` + +从 xref:#concurrent_flat_map[`concurrent_flat_map`][`concurrent++_++flat++_++map`] 移动构造。 `other` 的内部桶数组直接转移至新容器。哈希函数、谓词和分配器均从 `other` 移动构造。若统计功能 xref:#unordered_flat_map_boost_unordered_enable_stats[已启用] ,则转移 `other` 的内部统计信息,并调用 `other.reset++_++stats()` 。 + +[horizontal] +复杂度:常数时间。并发:阻塞 `other`。 + +--- + +==== 初始化列表构造函数 +[source,c++,subs="+quotes"] +---- +unordered_flat_map(std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `eql` 作为键相等性谓词、以及 `a` 作为分配器,并将 `il` 中的元素插入其中。 + +[horizontal] +要求;; 若使用默认值,则 `hasher` 、 `key++_++equal` 和 `allocator++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带分配器的桶数构造函数 +```c++ unordered_flat_map(size_type n, allocator_type const& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,使用默认的键相等谓词,以及 `a` 作为分配器。 + +[horizontal] +后置条件:`size() == 0` 要求:`hasher` 和 `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 带哈希函数和分配器的桶数构造函数 +```c++ unordered_flat_map(size_type n, hasher const& hf, allocator_type const& a); ``` + +unordered_flat_map(size_type n, hasher const& hf, allocator_type const& a); + +[horizontal] +后置条件:`size() == 0` 要求:`key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 带桶数和分配器的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_flat_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 作为分配器、以及默认的哈希函数和键相等性谓词,并将 `++[++f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:`hasher`、`key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 带桶数和哈希函数的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- + template + unordered_flat_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `a` 作为分配器以及默认的键相等性谓词,并将 `++[++f, l)` 范围内的元素插入其中。 + +[horizontal] +要求;; `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带分配器的初始化列表构造函数 + +```c++ unordered_flat_map(std::initializer_list il, const allocator_type& a); ``` + +构造一个空容器,使用 `a` 作为分配器、以及默认的哈希函数和键相等性谓词,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 带桶数和分配器的初始化列表构造函数 + +```c++ unordered_flat_map(std::initializer_list il, size_type n, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 作为分配器以及默认的哈希函数和键相等谓词,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 带桶数、哈希函数和分配器的初始化列表构造函数 + +```c++ unordered_flat_map(std::initializer_list il, size_type n, const hasher& hf, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、`a` 作为分配器以及默认的键相等谓词,并将 `il` 中的元素插入其中。 + +[horizontal] +要求;; `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +=== 析构函数 + +```c++ ~unordered_flat_map(); ``` + +[horizontal] +注意:析构函数会应用于每个元素,并且所有内存都会被释放。 + +--- + +=== 赋值操作 + +==== 复制赋值 + +```c++ unordered_flat_map& operator=(unordered_flat_map const& other); ``` + +赋值操作符。该操作会销毁容器中原有的元素,并从 other 复制赋值哈希函数与键相等性谓词。若 Alloc::propagate_on_container_copy_assignment 存在,且 Alloc::propagate_on_container_copy_assignment::value 为 true,则从 other 复制赋值分配器,最后插入 other 中所有元素的副本。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入^] 要求。 + +--- + +==== 移动赋值 +```c++ unordered_flat_map& operator=(unordered_flat_map&& other) noexcept((boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_move_assignment::value) && std::is_same::value); ``` 移动赋值运算符。销毁之前存在的元素,交换 other 中的哈希函数和谓词,若 Alloc::propagate_on_container_move_assignment 存在且 Alloc::propagate_on_container_move_assignment::value 为 true,则从 other 移动赋值分配器。若此时分配器与 other.get_allocator() 相等,则将 other 的内部桶数组直接转移至当前容器;否则,插入通过移动构造从 other 元素创建的副本。若统计功能已启用(参见 xref:unordered_flat_map_boost_unordered_enable_stats[相关说明]),则仅当最终分配器与 other.get_allocator() 相等时转移 other 的内部统计信息,且始终会调用 other.reset_stats()。 + +--- + +==== 初始化列表赋值 +```c++ unordered_flat_map& operator=(std::initializer_list il); ``` + +从初始化列表中的值进行赋值。所有之前存在的元素均被销毁。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入^] 要求。 + +=== 迭代器 + +==== begin +```c++ iterator begin() noexcept; const_iterator begin() const noexcept; ``` + +[horizontal] +返回:指向容器第一个元素的迭代器,若容器为空则返回容器尾后迭代器。复杂度:O(`bucket_count()`) + +--- + +==== end +```c++ iterator end() noexcept; const_iterator end() const noexcept; ``` + +[horizontal] +返回:指向容器尾后位置的迭代器。 + +--- + +==== cbegin +```c++ const_iterator cbegin() const noexcept; ``` + +[horizontal] +返回:指向容器第一个元素的 `const_iterator`,若容器为空,则返回容器尾后迭代器。复杂度:O(`bucket_count()`) + +--- + +==== cend +```c++ const_iterator cend() const noexcept; ``` + +[horizontal] +返回:指向容器尾后位置的 `const_iterator`。 + +--- + +=== 大小与容量 + +==== 空 + +```c++ [[nodiscard]] bool empty() const noexcept; ``` + +[horizontal] +返回: `size() == 0` + +--- + +==== 大小 + +```c++ size_type size() const noexcept; ``` + +[horizontal] +返回:`std::distance(begin(), end())` + +--- + +==== max++_++size + +```c++ size_type max_size() const noexcept; ``` + +[horizontal] +返回:可能的最大容器的 `size()`。 + +--- + +=== 修改器 + +==== 原地构造 +```c++ template std::pair emplace(Args&&... args); ``` + +当且仅当容器中没有等价的键时,插入一个使用参数 `args` 构造的对象。 + +[horizontal] +要求:`value_type` 可从 `args` 构造。返回:若执行了插入,则返回类型中的 `bool` 分量为 `true`。+ +若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。注意:可能使迭代器、指针和引用失效,但仅当插入导致负载大于最大负载时才会发生。+ +若 `args...` 的形式为 `k,v`,则仅在确定应插入元素时才构造整个对象,检查时仅使用 `k` 参数。 + +--- + +==== emplace++_++hint +```c++ template iterator emplace_hint(const_iterator position, Args&&... args); ``` + +当且仅当容器中没有等价的键时,插入一个使用参数 `args` 构造的对象。 + +`position` 是一个关于元素应插入位置的建议。此实现会忽略该建议。 + +[horizontal] +要求:`value_type` 可从 `args` 构造。返回:若执行了插入,则返回类型中的 `bool` 分量为 `true`。+ +若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。注意:可能使迭代器、指针和引用失效,但仅当插入导致负载大于最大负载时才会发生。+ +若 `args...` 的形式为 `k,v`,则仅在确定应插入元素时才构造整个对象,检查时仅使用 `k` 参数。 + +--- + +==== 复制插入 +```c++ std::pair insert(const value_type& obj); std::pair insert(const init_type& obj); ``` + +当且仅当容器中没有等价的键时,将 `obj` 插入容器。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入^] 要求。返回:若执行了插入,则返回类型中的 `bool` 分量为 `true`。+ +若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。注意:可能使迭代器、指针和引用失效,但仅当插入导致负载大于最大负载时才会发生。+ +形式为 `insert(x)` 的调用(其中 `x` 可同等转换为 `const value_type&` 和 `const init_type&`)不会产生歧义,并且会选择 `init_type` 重载。 + +--- + +==== 移动插入 +```c++ std::pair insert(value_type&& obj); std::pair insert(init_type&& obj); ``` + +当且仅当容器中没有等价的键时,将 `obj` 插入容器。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^] 要求。返回:若执行了插入,则返回类型中的 `bool` 分量为 `true`。+ +若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。注意:可能使迭代器、指针和引用失效,但仅当插入导致负载大于最大负载时才会发生。+ +形式为 `insert(x)` 的调用(其中 `x` 可同等转换为 `value_type&&` 和 `init_type&&`)不会产生歧义,并且会选择 `init_type` 重载。 + +--- + +==== 带提示的复制插入 +`iterator insert(const_iterator hint, const value_type& obj); iterator insert(const_iterator hint, const init_type& obj);` 当且仅当容器中没有等价的键时,将 `obj` 插入容器。 + +`hint` 是一个关于元素插入位置的提示,本实现将忽略该提示。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入^] 要求。返回:若执行了插入,则返回类型中的 `bool` 分量为 `true`。+ +若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。注意:可能使迭代器、指针和引用失效,但仅当插入导致负载大于最大负载时才会发生。+ +形式为 `insert(hint, x)` 的调用(其中 `x` 可同等转换为 `const value_type&` 和 `const init_type&`)不会产生歧义,并且会选择 `init_type` 重载。 + +--- + +==== 带提示的移动插入 +```c++ iterator insert(const_iterator hint, value_type&& obj); iterator insert(const_iterator hint, init_type&& obj); ``` + +当且仅当容器中没有等价的键时,将 `obj` 插入容器。 + +`hint` 是一个关于元素插入位置的提示,本实现将忽略该提示。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^] 要求。返回:若执行了插入,则返回类型中的 `bool` 分量为 `true`。+ +若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。注意:可能使迭代器、指针和引用失效,但仅当插入导致负载大于最大负载时才会发生。+ +形式为 `insert(hint, x)` 的调用(其中 `x` 可同等转换为 `value_type&&` 和 `init_type&&`)不会产生歧义,并且会选择 `init_type` 重载。 + +--- + +==== 迭代器范围插入 +```c++ template void insert(InputIterator first, InputIterator last); ``` + +将元素范围插入容器中。仅当容器中不存在等价键的元素时,才会插入相应元素。 + +[horizontal] +要求:`value_type` 需从 `*first` 处满足对容器的 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^] 要求。抛出:当插入单个元素时,若调用 `hasher` 以外的操作抛出异常,则函数无效果。注意:可能使迭代器、指针和引用失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== 初始化列表插入 +```c++ void insert(std::initializer_list); ``` + +将元素范围插入容器中。仅当容器中不存在等价键的元素时,才会插入相应元素。 + +[horizontal] +要求:`value_type` 需满足对容器而言的 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入^] 要求。抛出:当插入单个元素时,若调用 `hasher` 以外的操作抛出异常,则函数无效果。注意:可能使迭代器、指针和引用失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== try++_++emplace +```c++ template std::pair try_emplace(const key_type& k, Args&&... args); template std::pair try_emplace(key_type&& k, Args&&... args); template std::pair try_emplace(K&& k, Args&&... args); ``` + +如果容器中不存在键为 `k` 的元素,则向容器中插入一个新元素。 + +若容器中不存在键为 `k` 的元素,则插入一个新元素。 + +[horizontal] +返回:若执行了插入,则返回类型中的 `bool` 分量为 `true`。+ +若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。注意:此函数与 xref:#unordered_flat_map_emplace[emplace] 类似,区别在于:若存在等价的键,则不构造 `value_type`;否则,构造形式如下:+ + -- `c++` +// first two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) + +// third overload +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) ``` + +与 xref:#unordered_flat_map_emplace[emplace] 不同,后者只是将所有参数转发给 `value_type` 的构造函数。 + +可能会导致迭代器、指针和引用失效,但仅当插入操作导致负载因子超过最大负载因子时才会发生。 + +`template` 重载仅在以下条件下参与重载决议:`Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 均不能从 `K` 隐式转换。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。这实现了异构查找,从而避免实例化 `Key` 类型对象的开销。 + +-- + +--- + +==== 带提示的 try++_++emplace +```c++ template iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); template iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); template iterator try_emplace(const_iterator hint, K&& k, Args&&... args); ``` + +如果容器中不存在键为 `k` 的元素,则向容器中插入一个新元素。 + +若容器中不存在键为 `k` 的元素,则插入一个新元素。 + +`hint` 是一个关于元素应插入位置的建议。此实现会忽略该建议。 + +[horizontal] +返回:若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。注意:此函数与 xref:#unordered_flat_map_emplace_hint[emplace_hint] 类似,区别在于:若存在等价的键,则不构造 `value_type`;否则,构造形式如下:+ + -- `c++` +// first two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) + +// third overload +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) ``` + +而非像 xref:#unordered_flat_map_emplace_hint[emplace_hint] 只是简单地将所有参数转发给 value_type 的构造函数。 + +可能会导致迭代器、指针和引用失效,但仅当插入操作导致负载因子超过最大负载因子时才会发生。 + +`template` 重载仅在以下条件下参与重载决议:`Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 均不能从 `K` 隐式转换。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。这实现了异构查找,从而避免实例化 `Key` 类型对象的开销。 + +-- + +--- + +==== insert++_++or++_++assign +```c++ template std::pair insert_or_assign(const key_type& k, M&& obj); template std::pair insert_or_assign(key_type&& k, M&& obj); template std::pair insert_or_assign(K&& k, M&& obj); ``` + +向容器中插入一个新元素,或通过赋值给已包含的值来更新现有元素。 + +如果存在键为 k 的元素,则通过赋值 std::forward(obj) 来更新该元素 + +如果不存在这样的元素,则将其添加到容器中,形式如下:```c++ +// first two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(obj))) + +// third overload +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(obj))) ``` + +[horizontal] +返回:若执行了插入,则返回类型中的 `bool` 分量为 `true`。+ +若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。注意:可能会导致迭代器、指针和引用失效,但仅当插入操作导致负载因子超过最大负载因子时才会发生。+ +`template` 仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。这提供了异构查找能力,从而避免构造 `Key` 类型实例的开销。 + +--- + +==== 带提示的 insert++_++or++_++assign +```c++ template iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); template iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); template iterator insert_or_assign(const_iterator hint, K&& k, M&& obj); ``` + +向容器中插入一个新元素,或通过赋值给已包含的值来更新现有元素。 + +如果存在键为 k 的元素,则通过赋值 std::forward(obj) 来更新该元素 + +如果不存在这样的元素,则将其添加到容器中,形式如下:```c++ +// first two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(obj))) + +// third overload +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(obj))) ``` + +`hint` 是一个关于元素插入位置的提示,本实现将忽略该提示。 + +[horizontal] +返回:若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。注意:可能会导致迭代器、指针和引用失效,但仅当插入操作导致负载因子超过最大负载因子时才会发生。+ +`template` 仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。这提供了异构查找能力,从而避免构造 `Key` 类型实例的开销。 + +--- + + +==== 通过位置擦除 + +[source,c++,subs=+quotes] +---- +_convertible-to-iterator_ erase(iterator position); +_convertible-to-iterator_ erase(const_iterator position); +---- + +擦除由 `position` 指向的元素。 + +[horizontal] +返回;; 返回一个不透明对象,该对象可隐式转换为擦除前紧接在 `position` 之后的 `iterator` 或 `const++_++iterator` 。 抛出;; 无。 注意;; 返回的不透明对象必须被立即丢弃或转换为 `iterator` 或 `const++_++iterator` 。 + +--- + +==== 通过键擦除 +```c++ size_type erase(const key_type& k); template size_type erase(K&& k); ``` + +擦除所有键与 `k` 等价的元素。 + +[horizontal] +返回:被擦除的元素数量。抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出异常。注意:`template` 重载仅在以下条件下参与重载决议:`Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 均不能从 `K` 隐式转换。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。这提供了异构查找能力,从而避免构造 `Key` 类型实例的开销。 + +--- + +==== 范围擦除 + +```c++ iterator erase(const_iterator first, const_iterator last); ``` + +擦除从 `first` 到 `last` 范围内的元素。 + +[horizontal] +返回:指向被擦除元素之后位置的迭代器,即 `last`。抛出:在此实现中不会抛出异常(既不调用 `hasher` 也不调用 `key_equal` 对象)。 + +--- + +==== 交换 +```c++ void swap(unordered_flat_map& other) noexcept(boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_swap::value); ``` + +将容器的内容与参数进行交换。 + +如果 `Allocator::propagate_on_container_swap` 已声明且 `Allocator::propagate_on_container_swap::value` 为 `true`,则交换容器的分配器。否则,使用不相等的分配器进行交换将导致未定义行为。 + +[horizontal] +抛出:除非 `key_equal` 或 `hasher` 在交换时抛出异常,否则不会抛出异常。 + +--- + +==== pull +```c++ init_type pull(const_iterator position); ``` + +从 `position` 指向的元素移动构造一个 `init_value` 对象 `x`,擦除该元素并返回 `x`。 + +--- + +==== 清空 +```c++ void clear() noexcept; ``` + +擦除容器中的所有元素。 + +[horizontal] +后置条件:`size() == 0`,`max_load() >= max_load_factor() * bucket_count()` + +--- + +==== 合并 +```c++ template void merge(unordered_flat_map& source); template void merge(unordered_flat_map&& source); ``` + +尝试将 `source` 中所有键尚未出现在 `*this` 内的元素移动插入到 `*this` 中,同时将这些元素从 `source` 移除。 + +--- + +=== 观察器 + +==== get++_++allocator +``` allocator_type get_allocator() const noexcept; ``` + +[horizontal] +返回:容器的分配器。 + +--- + +==== 哈希函数 +``` hasher hash_function() const; ``` + +[horizontal] +返回:容器的哈希函数。 + +--- + +==== key++_++eq +``` key_equal key_eq() const; ``` + +[horizontal] +返回:容器的键相等谓词。 + +--- + +=== 查找 + +==== find +```c++ iterator find(const key_type& k); const_iterator find(const key_type& k) const; template iterator find(const K& k); + +``` + +[horizontal] +返回:指向键与 `k` 等价的元素的迭代器,若不存在这样的元素则返回 `end()`。注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + +--- + +==== count +```c++ size_type count(const key_type& k) const; template size_type count(const K& k) const; ``` + +[horizontal] +返回:键与 `k` 等价的元素数量。注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + +--- + +==== 包含 +```c++ bool contains(const key_type& k) const; template bool contains(const K& k) const; ``` + +[horizontal] +返回:一个布尔值,指示容器中是否存在键等于 `key` 的元素。注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + +--- + +==== equal++_++range +```c++ std::pair equal_range(const key_type& k); std::pair equal_range(const key_type& k) const; template std::pair equal_range(const K& k); template std::pair equal_range(const K& k) const; ``` + +[horizontal] +返回:一个布尔值,指示容器中是否存在键等于 `key` 的元素。注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + +--- + +==== operator++[]++ +```c++ mapped_type& operator[](const key_type& k); mapped_type& operator[](key_type&& k); template mapped_type& operator[](K&& k); ``` + +[horizontal] +效果:如果容器尚未包含键与 `k` 等价的元素,则插入值 `std::pair(k, mapped_type())`。返回:对 `x.second` 的引用,其中 `x` 是容器中已存在的元素,或键与 `k` 等价的新插入元素。抛出:如果调用 `hasher` 以外的操作抛出异常,则该函数无效果。注意:可能会导致迭代器、指针和引用失效,但仅当插入操作导致负载因子超过最大负载因子时才会发生。+ +`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + +--- + +==== at +```c++ mapped_type& at(const key_type& k); const mapped_type& at(const key_type& k) const; template mapped_type& at(const K& k); template const mapped_type& at(const K& k) const; ``` + +[horizontal] +返回:对 `x.second` 的引用,其中 `x` 是键与 `k` 等价的(唯一)元素。抛出:如果不存在这样的元素,则抛出 `std::out_of_range` 类型的异常对象。注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + +--- + +=== 桶接口 + +==== bucket_count +```c++ size_type bucket_count() const noexcept; ``` + +[horizontal] +返回:桶数组的大小。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ float load_factor() const noexcept; ``` + +[horizontal] +返回:`static_cast(size())/static_cast(bucket_count())`,若 `bucket_count() == 0` 则返回 `0`。 + +--- + +==== max++_++load++_++factor(最大负载因子) + +```c++ float max_load_factor() const noexcept; ``` + +[horizontal] +返回:容器的最大负载因子。 + +--- + +==== 设置最大负载因子 +```c++ void max_load_factor(float z); ``` + +[horizontal] +效果:不执行任何操作,因为用户不允许更改此参数。为与 `boost::unordered_map` 保持兼容而保留。 + +--- + + +==== max++_++load(最大负载) + +```c++ size_type max_load() const noexcept; ``` + +[horizontal] +返回:在不进行 rehash 的前提下,容器所能容纳的最大元素数量(假设不会有更多元素被擦除)。注意:在构造、rehash 或清空之后,容器的最大负载至少为 `max_load_factor() * bucket_count()`。在高负载条件下,该值可能因擦除操作而减小。 + +--- + +==== 重哈希 +```c++ void rehash(size_type n); ``` + +如有必要,将改变桶数组的大小,使其至少包含 `n` 个桶,并确保负载因子小于或等于最大负载因子。此操作将根据情况增加或减少容器的 `bucket++_++count()` 。 + +当 `size() == 0` 时,`rehash(0)` 将释放底层桶数组。如果提供的分配器使用花式指针,则随后会执行一次默认分配。 + +使迭代器、指针和引用失效,并改变元素的顺序。 + +[horizontal] +抛出:若抛出异常(除非由容器的哈希函数或比较函数抛出),则该函数无效果。 + +--- + +==== 保留 +```c++ void reserve(size_type n); ``` + +等价于 `a.rehash(ceil(n / a.max_load_factor()))`。 + +与 `rehash` 类似,该函数可用于增加或减少容器中的桶数量。 + +使迭代器、指针和引用失效,并改变元素的顺序。 + +[horizontal] +抛出:若抛出异常(除非由容器的哈希函数或比较函数抛出),则该函数无效果。 + +--- + +=== 统计信息 + +==== get++_++stats +```c++ stats get_stats() const; ``` + +[horizontal] +返回:对容器迄今为止执行的插入和查找操作的统计描述。注意:仅当 xref:reference/stats.adoc#stats[统计计算] 被 xref:unordered_flat_map_boost_unordered_enable_stats[启用] 时可用。 + +--- + +==== reset++_++stats +```c++ void reset_stats() noexcept; ``` + +[horizontal] +效果:将容器内部保持的统计信息重置为零。注意:仅当 xref:reference/stats.adoc#stats[统计计算] 被 xref:unordered_flat_map_boost_unordered_enable_stats[启用] 时可用。 + +--- + +=== 推导指引 +如果以下任何一条件为真,则推导指引将不参与重载决议: + +- 该推导指引包含 `InputIterator` 模板参数,且为此参数推导出的类型不符合输入迭代器的要求。 - 该推导指引包含 `Allocator` 模板参数,且为该参数推导出的类型不符合分配器要求。 - 该推导指引包含 `Hash` 模板参数,且为该参数推导出的类型为整型或符合分配器要求。 - 该推导指引包含 `Pred` 模板参数,且为该参数推导出的类型符合分配器要求。 + +推导指引中的 `size++_++type` 参数类型,指向由该推导指引所推导容器类型的 `size++_++type` 成员类型。其默认值与所选构造函数的默认值一致。 + +==== __iter-value-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-value-type__ = + typename std::iterator_traits::value_type; // exposition only +----- + +==== __iter-key-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-key-type__ = std::remove_const_t< + std::tuple_element_t<0, xref:#unordered_flat_map_iter_value_type[__iter-value-type__]>>; // exposition only +----- + +==== __iter-mapped-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-mapped-type__ = + std::tuple_element_t<1, xref:#unordered_flat_map_iter_value_type[__iter-value-type__]>; // exposition only +----- + +==== __iter-to-alloc-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-to-alloc-type__ = std::pair< + std::add_const_t>>, + std::tuple_element_t<1, xref:#unordered_flat_map_iter_value_type[__iter-value-type__]>>; // exposition only +----- + +=== 相等性比较 + +==== operator +```c++ template bool operator==(const unordered_flat_map& x, const unordered_flat_map& y); ``` + +若 `x.size() == y.size()` 且对于 `x` 中的每个元素,在 `y` 中均存在一个具有相同键且值相等(使用 `operator==` 比较值类型)的元素,则返回 `true`。 + +[horizontal] +注意:如果两个容器的相等谓词不等价,则行为未定义。 + +--- + +==== operator! +```c++ template bool operator!=(const unordered_flat_map& x, const unordered_flat_map& y); ``` + +若 `x.size() == y.size()` 且对于 `x` 中的每个元素,在 `y` 中均存在一个具有相同键且值相等(使用 `operator==` 比较值类型)的元素,则返回 `false`。 + +[horizontal] +注意:如果两个容器的相等谓词不等价,则行为未定义。 + +=== 交换 +```c++ template void swap(unordered_flat_map& x, unordered_flat_map& y) noexcept(noexcept(x.swap(y))); ``` + +交换 `x` 和 `y` 的内容。 + +如果 `Allocator::propagate_on_container_swap` 已声明且 `Allocator::propagate_on_container_swap::value` 为 `true`,则交换容器的分配器。否则,使用不相等的分配器进行交换将导致未定义行为。 + +[horizontal] +效果:`x.swap(y)` 抛出:除非 `key_equal` 或 `hasher` 在交换时抛出异常,否则不会抛出异常。 + +--- + +=== erase++_++if +```c++ template typename unordered_flat_map::size_type erase_if(unordered_flat_map& c, Predicate pred); ``` + +遍历容器 `c`,并移除所有使得给定谓词返回 `true` 的元素。 + +[horizontal] +返回:被擦除的元素数量。注意:等价于:`auto original_size = c.size(); for (auto i = c.begin(), last = c.end(); i != last; ) { if (pred(*i)) { i = c.erase(i); } else { ++i; } } return original_size - c.size();` 注意:传递给 `pred` 的引用是非常量的。 + +=== 序列化 + +`unordered++_++flat++_++map` 可通过本组件库提供的 API,借助 link:../../../../../serialization/index.html[Boost.Serialization] 进行归档/检索。支持常规归档与 XML 归档两种格式。 + +==== 将 unordered++_++flat++_++map 保存到归档 + +将 `unordered++_++flat++_++map` `x` 的所有元素保存到归档(XML 归档) `ar` 。 + +[horizontal] +要求:std::remove_const::type 和 std::remove_const::type 必须满足可序列化要求(XML 可序列化),且需要支持 Boost.Serialization 的 save_construct_data / load_construct_data 协议(该协议自动支持 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求)。 + +--- + +==== 从归档加载 unordered++_++flat++_++map + +删除 `unordered++_++flat++_++map` 容器 `x` 中所有已存在的元素,并从归档(XML 归档) `ar` 中插入原始 `unordered++_++flat++_++map` 容器 `other` 的元素副本,这些副本是从 `ar` 所读取的存储中恢复的。 + +[horizontal] +要求;; `x.key++_++equal()` 需要在功能上等价于 `other.key++_++equal()` 。 + +--- + +==== 将迭代器/常量迭代器保存到归档 + +将 `iterator` ( `const++_++iterator` )常量迭代器 `it` 的位置信息保存到归档(XML 归档) `ar` 中。 `it` 可以是 `end()` 迭代器。 + +[horizontal] +要求;; `it` 所指向的 `unordered++_++flat++_++map` 容器 `x` 必须先前已保存至 `ar` ,且在保存 `x` 与保存 `it` 期间不得对 `x` 执行任何修改操作。 + +--- + +==== 从归档加载迭代器/常量迭代器 + +使 `iterator` ( `const++_++iterator` ) `it` 指向原始 `iterator` ( `const++_++iterator` )所恢复的位置。该原始迭代器已被保存到由归档(XML 归档) `ar` 读取的存储中。 + +[horizontal] +要求;; 若 `x` 是 `it` 所指向的 `unordered++_++flat++_++map` 容器,则在加载 `x` 与加载 `it` 期间不得对 `x` 执行任何修改操作。 diff --git a/doc/modules/ROOT/pages/reference/unordered_flat_set_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/unordered_flat_set_zh_Hans.adoc new file mode 100644 index 0000000..90e4ced --- /dev/null +++ b/doc/modules/ROOT/pages/reference/unordered_flat_set_zh_Hans.adoc @@ -0,0 +1,1065 @@ +[#unordered_flat_set] +== 类模板 unordered_flat_set + +:idprefix: unordered_flat_set_ + +`boost::unordered_flat_set` — 一种开放定址无序关联容器,用于存储唯一值。 + +`boost::unordered_flat_set` 的性能远优于 `boost::unordered_set` 或 `std::unordered_set` 的其他实现。与基于节点的标准无序关联容器不同,`boost::unordered_flat_set` 的元素直接保存在桶数组中,当插入位置已被占用时,会转移到原始位置附近可用的桶中。这种数据布局称为**开放定址**。 + +由于采用开放定址,`boost::unordered_flat_set` 的接口在多个方面与 `boost::unordered_set`/`std::unordered_set` 存在差异: + +- `value_type` 必须可移动构造。 +- 在重哈希过程中不保持指针稳定性。 +- `begin()` 不是常数时间操作。 +- 没有用于桶操作的 API(除了 `bucket_count`),也没有节点提取/插入的 API。 +- 容器的最大负载因子由内部管理,用户无法设置。 + +除此之外,`boost::unordered_flat_set` 基本上是基于节点的标准无序关联容器的直接替代品。 + +=== 概要 + +[listing,subs="+macros,+quotes"] +----- +// #include xref:reference/header_unordered_flat_set.adoc[``] + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator> + class unordered_flat_set { + public: + // types + using key_type = Key; + using value_type = Key; + using init_type = Key; + using hasher = Hash; + using key_equal = Pred; + using allocator_type = Allocator; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + using iterator = _implementation-defined_; + using const_iterator = _implementation-defined_; + + using stats = xref:reference/stats.adoc#stats_stats_type[__stats-type__]; // if statistics are xref:unordered_flat_set_boost_unordered_enable_stats[enabled] + + // construct/copy/destroy + xref:#unordered_flat_set_default_constructor[unordered_flat_set](); + explicit xref:#unordered_flat_set_bucket_count_constructor[unordered_flat_set](size_type n, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + template + xref:#unordered_flat_set_iterator_range_constructor[unordered_flat_set](InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#unordered_flat_set_copy_constructor[unordered_flat_set](const unordered_flat_set& other); + xref:#unordered_flat_set_move_constructor[unordered_flat_set](unordered_flat_set&& other); + template + xref:#unordered_flat_set_iterator_range_constructor_with_allocator[unordered_flat_set](InputIterator f, InputIterator l, const allocator_type& a); + explicit xref:#unordered_flat_set_allocator_constructor[unordered_flat_set](const Allocator& a); + xref:#unordered_flat_set_copy_constructor_with_allocator[unordered_flat_set](const unordered_flat_set& other, const Allocator& a); + xref:#unordered_flat_set_move_constructor_from_concurrent_flat_set[unordered_flat_set](concurrent_flat_set&& other); + xref:#unordered_flat_set_initializer_list_constructor[unordered_flat_set](std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#unordered_flat_set_bucket_count_constructor_with_allocator[unordered_flat_set](size_type n, const allocator_type& a); + xref:#unordered_flat_set_bucket_count_constructor_with_hasher_and_allocator[unordered_flat_set](size_type n, const hasher& hf, const allocator_type& a); + template + xref:#unordered_flat_set_iterator_range_constructor_with_bucket_count_and_allocator[unordered_flat_set](InputIterator f, InputIterator l, size_type n, const allocator_type& a); + template + xref:#unordered_flat_set_iterator_range_constructor_with_bucket_count_and_hasher[unordered_flat_set](InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); + xref:#unordered_flat_set_initializer_list_constructor_with_allocator[unordered_flat_set](std::initializer_list il, const allocator_type& a); + xref:#unordered_flat_set_initializer_list_constructor_with_bucket_count_and_allocator[unordered_flat_set](std::initializer_list il, size_type n, + const allocator_type& a); + xref:#unordered_flat_set_initializer_list_constructor_with_bucket_count_and_hasher_and_allocator[unordered_flat_set](std::initializer_list il, size_type n, const hasher& hf, + const allocator_type& a); + xref:#unordered_flat_set_destructor[~unordered_flat_set](); + unordered_flat_set& xref:#unordered_flat_set_copy_assignment[operator++=++](const unordered_flat_set& other); + unordered_flat_set& xref:#unordered_flat_set_move_assignment[operator++=++](unordered_flat_set&& other) ++noexcept( + (boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_move_assignment::value) && + std::is_same::value);++ + unordered_flat_set& xref:#unordered_flat_set_initializer_list_assignment[operator++=++](std::initializer_list); + allocator_type xref:#unordered_flat_set_get_allocator[get_allocator]() const noexcept; + + // iterators + iterator xref:#unordered_flat_set_begin[begin]() noexcept; + const_iterator xref:#unordered_flat_set_begin[begin]() const noexcept; + iterator xref:#unordered_flat_set_end[end]() noexcept; + const_iterator xref:#unordered_flat_set_end[end]() const noexcept; + const_iterator xref:#unordered_flat_set_cbegin[cbegin]() const noexcept; + const_iterator xref:#unordered_flat_set_cend[cend]() const noexcept; + + // capacity + ++[[nodiscard]]++ bool xref:#unordered_flat_set_empty[empty]() const noexcept; + size_type xref:#unordered_flat_set_size[size]() const noexcept; + size_type xref:#unordered_flat_set_max_size[max_size]() const noexcept; + + // modifiers + template std::pair xref:#unordered_flat_set_emplace[emplace](Args&&... args); + template iterator xref:#unordered_flat_set_emplace_hint[emplace_hint](const_iterator position, Args&&... args); + std::pair xref:#unordered_flat_set_copy_insert[insert](const value_type& obj); + std::pair xref:#unordered_flat_set_move_insert[insert](value_type&& obj); + template std::pair xref:#unordered_flat_set_transparent_insert[insert](K&& k); + iterator xref:#unordered_flat_set_copy_insert_with_hint[insert](const_iterator hint, const value_type& obj); + iterator xref:#unordered_flat_set_move_insert_with_hint[insert](const_iterator hint, value_type&& obj); + template iterator xref:#unordered_flat_set_transparent_insert_with_hint[insert](const_iterator hint, K&& k); + template void xref:#unordered_flat_set_insert_iterator_range[insert](InputIterator first, InputIterator last); + void xref:#unordered_flat_set_insert_initializer_list[insert](std::initializer_list); + + _convertible-to-iterator_ xref:#unordered_flat_set_erase_by_position[erase](iterator position); + _convertible-to-iterator_ xref:#unordered_flat_set_erase_by_position[erase](const_iterator position); + size_type xref:#unordered_flat_set_erase_by_key[erase](const key_type& k); + template size_type xref:#unordered_flat_set_erase_by_key[erase](K&& k); + iterator xref:#unordered_flat_set_erase_range[erase](const_iterator first, const_iterator last); + void xref:#unordered_flat_set_swap[swap](unordered_flat_set& other) + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_swap::value); + init_type xref:#unordered_flat_set_pull[pull](const_iterator position); + void xref:#unordered_flat_set_clear[clear]() noexcept; + + template + void xref:#unordered_flat_set_merge[merge](unordered_flat_set& source); + template + void xref:#unordered_flat_set_merge[merge](unordered_flat_set&& source); + + // observers + hasher xref:#unordered_flat_set_hash_function[hash_function]() const; + key_equal xref:#unordered_flat_set_key_eq[key_eq]() const; + + // set operations + iterator xref:#unordered_flat_set_find[find](const key_type& k); + const_iterator xref:#unordered_flat_set_find[find](const key_type& k) const; + template + iterator xref:#unordered_flat_set_find[find](const K& k); + template + const_iterator xref:#unordered_flat_set_find[find](const K& k) const; + size_type xref:#unordered_flat_set_count[count](const key_type& k) const; + template + size_type xref:#unordered_flat_set_count[count](const K& k) const; + bool xref:#unordered_flat_set_contains[contains](const key_type& k) const; + template + bool xref:#unordered_flat_set_contains[contains](const K& k) const; + std::pair xref:#unordered_flat_set_equal_range[equal_range](const key_type& k); + std::pair xref:#unordered_flat_set_equal_range[equal_range](const key_type& k) const; + template + std::pair xref:#unordered_flat_set_equal_range[equal_range](const K& k); + template + std::pair xref:#unordered_flat_set_equal_range[equal_range](const K& k) const; + + // bucket interface + size_type xref:#unordered_flat_set_bucket_count[bucket_count]() const noexcept; + + // hash policy + float xref:#unordered_flat_set_load_factor[load_factor]() const noexcept; + float xref:#unordered_flat_set_max_load_factor[max_load_factor]() const noexcept; + void xref:#unordered_flat_set_set_max_load_factor[max_load_factor](float z); + size_type xref:#unordered_flat_set_max_load[max_load]() const noexcept; + void xref:#unordered_flat_set_rehash[rehash](size_type n); + void xref:#unordered_flat_set_reserve[reserve](size_type n); + + // statistics (if xref:unordered_flat_set_boost_unordered_enable_stats[enabled]) + stats xref:#unordered_flat_set_get_stats[get_stats]() const; + void xref:#unordered_flat_set_reset_stats[reset_stats]() noexcept; + }; + + // Deduction Guides + template>, + class Pred = std::equal_to>, + class Allocator = std::allocator>> + unordered_flat_set(InputIterator, InputIterator, typename xref:#unordered_flat_set_deduction_guides[__see below__]::size_type = xref:#unordered_flat_set_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_flat_set, Hash, Pred, Allocator>; + + template, class Pred = std::equal_to, + class Allocator = std::allocator> + unordered_flat_set(std::initializer_list, typename xref:#unordered_flat_set_deduction_guides[__see below__]::size_type = xref:#unordered_flat_set_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_flat_set; + + template + unordered_flat_set(InputIterator, InputIterator, typename xref:#unordered_flat_set_deduction_guides[__see below__]::size_type, Allocator) + -> unordered_flat_set, + boost::hash>, + std::equal_to>, Allocator>; + + template + unordered_flat_set(InputIterator, InputIterator, Allocator) + -> unordered_flat_set, + boost::hash>, + std::equal_to>, Allocator>; + + template + unordered_flat_set(InputIterator, InputIterator, typename xref:#unordered_flat_set_deduction_guides[__see below__]::size_type, Hash, + Allocator) + -> unordered_flat_set, Hash, + std::equal_to>, Allocator>; + + template + unordered_flat_set(std::initializer_list, typename xref:#unordered_flat_set_deduction_guides[__see below__]::size_type, Allocator) + -> unordered_flat_set, std::equal_to, Allocator>; + + template + unordered_flat_set(std::initializer_list, Allocator) + -> unordered_flat_set, std::equal_to, Allocator>; + + template + unordered_flat_set(std::initializer_list, typename xref:#unordered_flat_set_deduction_guides[__see below__]::size_type, Hash, Allocator) + -> unordered_flat_set, Allocator>; + +} // namespace unordered +} // namespace boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_Key_ +|`Key` must be https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^] into the container +并且可以从容器中 https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^]。 + +|_Hash_ +|A unary function object type that acts a hash function for a `Key`. It takes a single argument of type `Key` and returns a value of type `std::size_t`. + +|_Pred_ +|A binary function object that induces an equivalence relation on values of type `Key`. It takes two arguments of type `Key` and returns a value of type `bool`. + +|_Allocator_ +|An allocator whose value type is the same as the container's value type. +支持使用https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] 的分配器。 + +|=== + +容器的元素保存在内部的**桶数组**中。元素根据其哈希码被插入到对应的桶中,但如果该桶已被占用(发生**冲突**),则会使用原始位置附近的一个可用桶。 + +桶数组的大小可以通过调用 `insert`/`emplace` 自动增加,或者作为调用 `rehash`/`reserve` 的结果而增加。容器的**负载因子**(元素个数除以桶数量)永远不会大于 `max_load_factor()`,但在容量较小的情况下,实现可能会允许更高的负载。 + +如果`link:../../../../../container_hash/doc/html/hash.html#ref_hash_is_avalanching[hash_is_avalanching]::value` 为 `true`,则哈希函数将按原样使用;否则,将添加一个比特混合后处理阶段,以增加哈希质量,但会增加额外的计算成本。 + +--- + +=== 配置宏 + +==== `BOOST_UNORDERED_ENABLE_STATS` + +全局定义此宏以启用容器的 xref:reference/stats.adoc#stats[统计信息计算]。请注意,此选项会降低许多操作的整体性能。 + +--- + +=== 类型定义 + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ iterator; +---- + +一种常量迭代器,其值类型为 `value_type`。 + +迭代器类别至少为前向迭代器。 + +可转换为 `const_iterator`。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ const_iterator; +---- + +一种常量迭代器,其值类型为 `value_type`。 + +迭代器类别至少为前向迭代器。 + +=== 构造函数 + +==== 默认构造函数 +```c++ unordered_flat_set(); ``` + +使用 `hasher()` 作为哈希函数、`key_equal()` 作为键相等谓词以及 `allocator_type()` 作为分配器,构造一个空容器。 + +[horizontal] +后置条件:;; `size() == 0` +要求:;; 如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要是 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 桶数构造函数 +```c++ explicit unordered_flat_set(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器。 + +[horizontal] +后置条件:;; `size() == 0` +要求:;; 如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要是 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_flat_set(InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:;; 如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要是 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 复制构造函数 +```c++ unordered_flat_set(unordered_flat_set const& other); ``` + +复制构造函数。复制所包含的元素、哈希函数、谓词和分配器。 + +如果 `Allocator::select_on_container_copy_construction` 存在且具有正确的签名,则分配器将根据其结果进行构造。 + +[horizontal] +要求:;; `value_type` 可复制构造。 + +--- + +==== 移动构造函数 +```c++ unordered_flat_set(unordered_flat_set&& other); ``` + +移动构造函数。`other` 的内部桶数组将直接转移给新容器。哈希函数、谓词和分配器从 `other` 进行移动构造。如果启用了统计信息(xref:unordered_flat_set_boost_unordered_enable_stats[enabled]),则从 `other` 转移内部统计信息,并调用 `other.reset_stats()`。 + +--- + +==== 带分配器的迭代器区间构造函数 +```c++ template unordered_flat_set(InputIterator f, InputIterator l, const allocator_type& a); ``` + +构造一个以 `a` 为分配器的空容器,使用默认的哈希函数和键相等谓词,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:;; `hasher` 和 `key_equal` 需要是 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 分配器构造函数 +```c++ explicit unordered_flat_set(Allocator const& a); ``` + +构造一个空容器,使用分配器 `a`。 + +--- + +==== 带分配器的复制构造函数 +```c++ unordered_flat_set(unordered_flat_set const& other, Allocator const& a); ``` + +构造一个容器,复制 `other` 所包含的元素、哈希函数和谓词,但使用分配器 `a`。 + +--- + +==== 带分配器的移动构造函数 +```c++ unordered_flat_set(unordered_flat_set&& other, Allocator const& a); ``` + +如果 `a == other.get_allocator()`,则 `other` 的元素会直接转移给新容器;否则,将根据 `other` 中的元素进行移动构造。哈希函数和谓词从 `other` 进行移动构造,而分配器则从 `a` 进行复制构造。如果启用了统计信息(xref:unordered_flat_set_boost_unordered_enable_stats[enabled]),则仅当 `a == other.get_allocator()` 时才从 `other` 转移内部统计信息,并且总是调用 `other.reset_stats()`。 + +--- + +==== 从 concurrent_flat_set 移动构造 + +```c++ unordered_flat_set(concurrent_flat_set&& other); ``` + +从 xref:#concurrent_flat_set[`concurrent_flat_set`] 进行移动构造。`other` 的内部桶数组将直接转移给新容器。哈希函数、谓词和分配器从 `other` 进行移动构造。如果启用了统计信息(xref:unordered_flat_set_boost_unordered_enable_stats[enabled]),则从 `other` 转移内部统计信息,并调用 `other.reset_stats()`。 + +[horizontal] +复杂度:;; 常数时间。 +并发性:;; 在 `other` 上阻塞。 + +--- + +==== 初始化列表构造函数 +[source,c++,subs="+quotes"] +---- +unordered_flat_set(std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:;; 如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要是 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 带分配器的桶数构造函数 +```c++ unordered_flat_set(size_type n, allocator_type const& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,默认的哈希函数和键相等谓词,并以 `a` 作为分配器。 + +[horizontal] +后置条件:;; `size() == 0` +要求:;; `hasher` 和 `key_equal` 需要是 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 带哈希函数和分配器的桶数构造函数 +```c++ unordered_flat_set(size_type n, hasher const& hf, allocator_type const& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,默认的键相等谓词,并以 `a` 作为分配器。 + +[horizontal] +后置条件:;; `size() == 0` +要求:;; `key_equal` 需要是 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 带桶数和分配器的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_flat_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 作为分配器以及默认的哈希函数和键相等谓词,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:;; `hasher` 和 `key_equal` 需要是 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 带桶数和哈希函数的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- + template + unordered_flat_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`a` 作为分配器,并使用默认的键相等谓词,然后将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:;; `key_equal` 需要是 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 带分配器的初始化列表构造函数 + +```c++ unordered_flat_set(std::initializer_list il, const allocator_type& a); ``` + +使用 `a` 以及默认的哈希函数和键相等谓词构造一个空容器,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:;; `hasher` 和 `key_equal` 需要是 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 带桶数和分配器的初始化列表构造函数 + +```c++ unordered_flat_set(std::initializer_list il, size_type n, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 以及默认的哈希函数和键相等谓词,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:;; `hasher` 和 `key_equal` 需要是 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +==== 带桶数、哈希函数和分配器的初始化列表构造函数 + +```c++ unordered_flat_set(std::initializer_list il, size_type n, const hasher& hf, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`a` 作为分配器,以及默认的键相等谓词,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:;; `key_equal` 需要是 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]。 + +--- + +=== 析构函数 + +```c++ ~unordered_flat_set(); ``` + +[horizontal] +注意:;; 析构函数会应用于每个元素,并且所有内存都会被释放。 + +--- + +=== 赋值操作 + +==== 复制赋值 + +```c++ unordered_flat_set& operator=(unordered_flat_set const& other); ``` + +赋值运算符。销毁先前存在的元素,从 `other` 复制赋值哈希函数和谓词,如果 `Alloc::propagate_on_container_copy_assignment` 存在且 `Alloc::propagate_on_container_copy_assignment::value` 为 `true`,则从 `other` 复制赋值分配器,最后插入 `other` 元素的副本。 + +[horizontal] +要求:;; `value_type` 必须是 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^]。 + +--- + +==== 移动赋值 +```c++ +unordered_flat_set& operator=(unordered_flat_set&& other) noexcept((boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_move_assignment::value) && std::is_same::value); +``` +移动赋值运算符。销毁先前存在的元素,交换来自 `other` 的哈希函数和谓词,如果 `Alloc::propagate_on_container_move_assignment` 存在且 `Alloc::propagate_on_container_move_assignment::value` 为 `true`,则从 `other` 移动赋值分配器。如果此时分配器等于 `other.get_allocator()`,则 `other` 的内部桶数组直接转移给当前容器;否则,插入 `other` 元素的移动构造副本。如果启用了统计信息(xref:unordered_flat_set_boost_unordered_enable_stats[enabled]),则仅当最终的分配器等于 `other.get_allocator()` 时才从 `other` 转移内部统计信息,并且总是调用 `other.reset_stats()`。 + +--- + +==== 初始化列表赋值 +```c++ unordered_flat_set& operator=(std::initializer_list il); ``` + +从初始化列表中的值进行赋值。所有先前存在的元素均被销毁。 + +[horizontal] +要求:;; `value_type` 必须是 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^]。 + +=== 迭代器 + +==== begin +```c++ iterator begin() noexcept; const_iterator begin() const noexcept; ``` + +[horizontal] +返回:;; 指向容器第一个元素的迭代器,若容器为空,则返回容器的尾后迭代器。 +复杂度:;; O(`bucket_count()`) + +--- + +==== end +```c++ iterator end() noexcept; const_iterator end() const noexcept; ``` + +[horizontal] +返回:;; 指向容器尾后值的迭代器。 + +--- + +==== cbegin +```c++ const_iterator cbegin() const noexcept; ``` + +[horizontal] +返回:;; 指向容器第一个元素的常量迭代器,若容器为空,则返回容器的尾后常量迭代器。 +复杂度:;; O(`bucket_count()`) + +--- + +==== cend +```c++ const_iterator cend() const noexcept; ``` + +[horizontal] +返回:;; 指向容器尾后值的常量迭代器。 + +--- + +=== 大小与容量 + +==== 空 + +```c++ [[nodiscard]] bool empty() const noexcept; ``` + +[horizontal] +返回:;; `size() == 0` + +--- + +==== 大小 + +```c++ size_type size() const noexcept; ``` + +[horizontal] +返回:;; `std::distance(begin(), end())` + +--- + +==== max_size + +```c++ size_type max_size() const noexcept; ``` + +[horizontal] +返回:;; 可能的最大容器的 `size()`。 + +--- + +=== 修改器 + +==== 原地构造 +```c++ template std::pair emplace(Args&&... args); ``` + +当且仅当容器中不存在具有等价键的元素时,才插入一个使用参数 `args` 构造的对象。 + +[horizontal] +要求:;; `value_type` 可以从 `args` 构造。 +返回:;; 如果进行了插入,则返回类型的 `bool` 分量为 `true`。 +如果进行了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:;; 如果除调用 `hasher` 之外的操作抛出异常,则该函数无效。 +备注:;; 可能会使迭代器、指针和引用失效,但仅当插入导致负载因子大于最大负载因子时才会发生。 + +--- + +==== emplace_hint +```c++ template iterator emplace_hint(const_iterator position, Args&&... args); ``` + +当且仅当容器中不存在具有等价键的元素时,才插入一个使用参数 `args` 构造的对象。 + +`position` 是关于元素应该插入位置的一个提示。此实现会忽略它。 + +[horizontal] +要求:;; `value_type` 可以从 `args` 构造。 +返回:;; 如果进行了插入,则返回类型的 `bool` 分量为 `true`。 +如果进行了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:;; 如果除调用 `hasher` 之外的操作抛出异常,则该函数无效。 +备注:;; 可能会使迭代器、指针和引用失效,但仅当插入导致负载因子大于最大负载因子时才会发生。 + +--- + +==== 复制插入 +```c++ std::pair insert(const value_type& obj); ``` + +当且仅当容器中不存在具有等价键的元素时,才将 `obj` 插入容器中。 + +[horizontal] +要求:;; `value_type` 必须是 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^]。 +返回:;; 如果进行了插入,则返回类型的 `bool` 分量为 `true`。 +如果进行了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:;; 如果除调用 `hasher` 之外的操作抛出异常,则该函数无效。 +备注:;; 可能会使迭代器、指针和引用失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== 移动插入 +```c++ std::pair insert(value_type&& obj); ``` + +当且仅当容器中不存在具有等价键的元素时,才将 `obj` 插入容器中。 + +[horizontal] +要求:;; `value_type` 必须是 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^]。 +返回:;; 如果进行了插入,则返回类型的 `bool` 分量为 `true`。 +如果进行了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:;; 如果除调用 `hasher` 之外的操作抛出异常,则该函数无效。 +备注:;; 可能会使迭代器、指针和引用失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== 透明插入 +```c++ template std::pair insert(K&& k); ``` + +当且仅当容器中不存在具有等价键的元素时,才插入一个从 `std::forward(k)` 构造的元素。 + +[horizontal] +要求:;; `value_type` 可以从 `k` 进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^]。 +返回:;; 如果进行了插入,则返回类型的 bool 分量为 true。 +如果进行了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:;; 如果除调用 `hasher` 之外的操作抛出异常,则该函数无效。 +备注:;; 可能会使迭代器、指针和引用失效,但仅当插入导致负载大于最大负载时才会发生。 +此外,仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,并且 `K` 不能隐式转换为 `iterator` 或 `const_iterator` 时,此重载才会参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 类型调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型的开销。 + +--- + +==== 带提示的复制插入 +```c++iterator insert(const_iterator hint, const value_type& obj); +```当且仅当容器中不存在具有等价键的元素时,才将 `obj` 插入容器中。 + +`hint` 是关于元素应该插入位置的一个提示。此实现会忽略它。 + +[horizontal] +要求:;; `value_type` 必须是 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^]。 +返回:;; 如果进行了插入,则返回类型的 `bool` 分量为 `true`。 +如果进行了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:;; 如果除调用 `hasher` 之外的操作抛出异常,则该函数无效。 +备注:;; 可能会使迭代器、指针和引用失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== 带提示的移动插入 +```c++ iterator insert(const_iterator hint, value_type&& obj); ``` + +当且仅当容器中不存在具有等价键的元素时,才将 `obj` 插入容器中。 + +`hint` 是关于元素应该插入位置的一个提示。此实现会忽略它。 + +[horizontal] +要求:;; `value_type` 必须是 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^]。 +返回:;; 如果进行了插入,则返回类型的 `bool` 分量为 `true`。 +如果进行了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:;; 如果除调用 `hasher` 之外的操作抛出异常,则该函数无效。 +备注:;; 可能会使迭代器、指针和引用失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== 带提示的透明插入 +```c++ template std::pair insert(const_iterator hint, K&& k); ``` + +当且仅当容器中不存在具有等价键的元素时,才插入一个从 `std::forward(k)` 构造的元素。 + +`hint` 是关于元素应该插入位置的一个提示。此实现会忽略它。 + +[horizontal] +要求:;; `value_type` 可以从 `k` 进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^]。 +返回:;; 如果进行了插入,则返回类型的 bool 分量为 true。 +如果进行了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:;; 如果除调用 `hasher` 之外的操作抛出异常,则该函数无效。 +备注:;; 可能会使迭代器、指针和引用失效,但仅当插入导致负载大于最大负载时才会发生。 +此外,仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,并且 `K` 不能隐式转换为 `iterator` 或 `const_iterator` 时,此重载才会参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 类型调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型的开销。 + +--- + +==== 迭代器范围插入 +```c++ template void insert(InputIterator first, InputIterator last); ``` + +将一系列元素插入容器中。当且仅当容器中不存在具有等价键的元素时,才插入该元素。 + +[horizontal] +要求:;; `value_type` 可以从 `*first` 进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^] 到容器中。 +抛出:;; 当插入单个元素时,如果除调用 `hasher` 之外的操作抛出异常,则该函数无效。 +备注:;; 可能会使迭代器、指针和引用失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== 初始化列表插入 +```c++ void insert(std::initializer_list); ``` + +将一系列元素插入容器中。当且仅当容器中不存在具有等价键的元素时,才插入该元素。 + +[horizontal] +要求:;; `value_type` 必须可以 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^] 到容器中。抛出:;; 当插入单个元素时,如果除调用 `hasher` 之外的操作抛出异常,则该函数无效。备注:;; 可能会使迭代器、指针和引用失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== 通过位置擦除 + +[source,c++,subs=+quotes] +---- +_convertible-to-iterator_ erase(iterator position); +_convertible-to-iterator_ erase(const_iterator position); +---- + +擦除由 `position` 指向的元素。 + +[horizontal] +返回:;; 一个不透明对象,可隐式转换为擦除前紧接 `position` 之后的 `iterator` 或 `const_iterator`。抛出:;; 无。备注:;; 返回的不透明对象只能被丢弃或立即转换为 `iterator` 或 `const_iterator`。 + +--- + +==== 通过键擦除 +```c++ size_type erase(const key_type& k); template size_type erase(K&& k); ``` + +删除所有键与 `k` 等价的元素。 + +[horizontal] +返回:;; 被删除的元素数量。抛出:;; 仅当由 `hasher` 或 `key_equal` 抛出异常时才会抛出异常。备注:;; `template` 重载仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,并且 `K` 不能隐式转换为 `iterator` 或 `const_iterator` 时,才会参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 类型调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型的开销。 + +--- + +==== 范围擦除 + +```c++ iterator erase(const_iterator first, const_iterator last); ``` + +删除从 `first` 到 `last` 范围内的元素。 + +[horizontal] +返回:;; 被删除元素之后的迭代器,即 `last`。抛出:;; 在此实现中不抛出任何异常(既不会调用 `hasher` 也不会调用 `key_equal` 对象)。 + +--- + +==== 交换 +```c++ void swap(unordered_flat_set& other) noexcept(boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_swap::value); ``` + +交换容器与参数的内容。 + +如果 `Allocator::propagate_on_container_swap` 已声明且 `Allocator::propagate_on_container_swap::value` 为 `true`,则交换两个容器的分配器。否则,使用不相等的分配器进行交换将导致未定义行为。 + +[horizontal] +抛出:;; 除非 `key_equal` 或 `hasher` 在交换时抛出异常,否则不抛出任何异常。 + +--- + +==== pull +```c++ init_type pull(const_iterator position); ``` + +从 `position` 指向的元素移动构造一个 `init_value` `x`,删除该元素并返回 `x`。 + +--- + +==== 清空 +```c++ void clear() noexcept; ``` + +清除容器中的所有元素。 + +[horizontal] +Postconditions:;; `size() == 0`, `max_load() >= max_load_factor() * bucket_count()` + +--- + +==== 合并 +```c++ template void merge(unordered_flat_set& source); template void merge(unordered_flat_set&& source); ``` + +移动插入 `source` 中所有键尚未存在于 `*this` 中的元素,并从 `source` 中删除它们。 + +--- + +=== 观察器 + +==== get_allocator +``` allocator_type get_allocator() const noexcept; ``` + +[horizontal] +返回:;; 容器的分配器。 + +--- + +==== 哈希函数 +``` hasher hash_function() const; ``` + +[horizontal] +返回:;; 容器的哈希函数。 + +--- + +==== key_eq +``` key_equal key_eq() const; ``` + +[horizontal] +返回:;; 容器的键相等谓词。 + +--- + +=== 查找 + +==== find +```c++ iterator find(const key_type& k); const_iterator find(const key_type& k) const; template iterator find(const K& k); + +``` + +[horizontal] +返回:;; 指向键与 `k` 等价之元素的迭代器,若无此元素则返回 `end()`。备注:;; `template` 重载仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时才会参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 类型调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型的开销。 + +--- + +==== count +```c++ size_type count(const key_type& k) const; template size_type count(const K& k) const; ``` + +[horizontal] +返回:;; 键与 `k` 等价的元素个数。备注:;; `template` 重载仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时才会参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 类型调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型的开销。 + +--- + +==== 包含 +```c++ bool contains(const key_type& k) const; template bool contains(const K& k) const; ``` + +[horizontal] +返回:;; 一个布尔值,指示容器中是否存在键等于 `key` 的元素。备注:;; `template` 重载仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时才会参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 类型调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型的开销。 + +--- + +==== equal_range +```c++ std::pair equal_range(const key_type& k); std::pair equal_range(const key_type& k) const; template std::pair equal_range(const K& k); template std::pair equal_range(const K& k) const; ``` + +[horizontal] +返回:;; 一个范围,包含所有键与 `k` 等价的元素。若容器中不包含任何此类元素,则返回 `std::make_pair(b.end(), b.end())`。备注:;; `template` 重载仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时才会参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 类型调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型的开销。 + +--- + +=== 桶接口 + +==== bucket_count +```c++ size_type bucket_count() const noexcept; ``` + +[horizontal] +返回:;; 桶数组的大小。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ float load_factor() const noexcept; ``` + +[horizontal] +返回:;; `static_cast(size())/static_cast(bucket_count())`,如果 `bucket_count() == 0` 则返回 `0`。 + +--- + +==== 最大负载因子 + +```c++ float max_load_factor() const noexcept; ``` + +[horizontal] +返回:;; 返回容器的最大负载因子。 + +--- + +==== 设置最大负载因子 +```c++ void max_load_factor(float z); ``` + +[horizontal] +效果:;; 不执行任何操作,因为用户不允许更改此参数。为与 `boost::unordered_set` 保持兼容而保留。 + +--- + + +==== 最大负载 + +```c++ size_type max_load() const noexcept; ``` + +[horizontal] +返回:;; 假设不再删除更多元素,容器在不进行重哈希的情况下所能容纳的最大元素数量。注意:;; 在构造、重哈希或清空之后,容器的最大负载至少为 `max_load_factor() * bucket_count()`。在高负载条件下,该值可能因删除操作而减小。 + +--- + +==== 重哈希 +```c++ void rehash(size_type n); ``` + +必要时改变桶数组的大小,使得至少包含 `n` 个桶,并且负载因子小于或等于最大负载因子。在适用的情况下,这将增大或缩小与容器相关联的 `bucket_count()`。 + +当 `size() == 0` 时,`rehash(0)` 将释放底层桶数组的内存。如果所提供的分配器使用 fancy pointers,随后将执行一次默认分配。 + +使迭代器、指针和引用失效,并改变元素的顺序。 + +[horizontal] +抛出:;; 如果抛出异常(除非是由容器的哈希函数或比较函数抛出),则该函数无效。 + +--- + +==== 保留 +```c++ void reserve(size_type n); ``` + +等价于 `a.rehash(ceil(n / a.max_load_factor()))`。 + +与 `rehash` 类似,此函数可用于增加或减少容器中桶的数量。 + +使迭代器、指针和引用失效,并改变元素的顺序。 + +[horizontal] +抛出:;; 如果抛出异常(除非是由容器的哈希函数或比较函数抛出),则该函数无效。 + +--- + +=== 统计信息 + +==== get_stats +```c++ stats get_stats() const; ``` + +[horizontal] +返回:;; 对容器迄今为止所执行的插入和查找操作的统计描述。备注:;; 仅在 xref:reference/stats.adoc#stats[统计信息计算] 已 xref:unordered_flat_set_boost_unordered_enable_stats[启用] 时可用。 + +--- + +==== reset_stats +```c++ void reset_stats() noexcept; ``` + +[horizontal] +效果:;; 将容器内部保持的统计信息归零。备注:;; 仅在 xref:reference/stats.adoc#stats[统计信息计算] 已 xref:unordered_flat_set_boost_unordered_enable_stats[启用] 时可用。 + +--- + +=== 推导指引 +在以下任一条件成立时,推导指引将不参与重载决议: + +- 它具有 `InputIterator` 模板参数,并且为该参数推导出的类型不符合输入迭代器的要求。- 它具有 `Allocator` 模板参数,并且为该参数推导出的类型不符合分配器的要求。- 它具有 `Hash` 模板参数,并且为该参数推导出的是整数类型或符合分配器要求的类型。- 它具有 `Pred` 模板参数,并且为该参数推导出的是符合分配器要求的类型。 + +推导指引中的 `size_type` 参数类型指向由该推导指引所推导出的容器类型的 `size_type` 成员类型。其默认值与所选构造函数的默认值一致。 + +==== _iter-value-type_ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-value-type__ = + typename std::iterator_traits::value_type; // exposition only +----- + +=== 相等性比较 + +==== operator +```c++ template bool operator==(const unordered_flat_set& x, const unordered_flat_set& y); ``` + +如果 `x.size() == y.size()` 并且对于 `x` 中的每个元素,在 `y` 中都有一个具有相同键且值相等的元素(使用 `operator==` 比较值类型),则返回 `true`。 + +[horizontal] +备注:;; 如果两个容器不具有等价的相等谓词,则行为未定义。 + +--- + +==== operator! +```c++ template bool operator!=(const unordered_flat_set& x, const unordered_flat_set& y); ``` + +如果 `x.size() == y.size()` 并且对于 `x` 中的每个元素,在 `y` 中都有一个具有相同键且值相等的元素(使用 `operator==` 比较值类型),则返回 `false`。 + +[horizontal] +备注:;; 如果两个容器不具有等价的相等谓词,则行为未定义。 + +=== 交换 +```c++ template void swap(unordered_flat_set& x, unordered_flat_set& y) noexcept(noexcept(x.swap(y))); ``` + +交换 `x` 和 `y` 的内容。 + +如果 `Allocator::propagate_on_container_swap` 已声明且 `Allocator::propagate_on_container_swap::value` 为 `true`,则交换两个容器的分配器。否则,使用不相等的分配器进行交换将导致未定义行为。 + +[horizontal] +效果:;; `x.swap(y)` 抛出:;; 除非 `key_equal` 或 `hasher` 在交换时抛出异常,否则不抛出任何异常。 + +--- + +=== erase_if +```c++ template typename unordered_flat_set::size_type erase_if(unordered_flat_set& c, Predicate pred); ``` + +遍历容器 `c` 并移除所有使给定谓词返回 `true` 的元素。 + +[horizontal] +返回:;; 被移除的元素数量。备注:;; 等价于:```c++ auto original_size = c.size(); for (auto i = c.begin(), last = c.end(); i != last; ) { if (pred(*i)) { i = c.erase(i); } else { ++i; } } return original_size - c.size(); ``` + +=== 序列化 + +`unordered_flat_set` 可以通过本库提供的 API,使用 link:../../../../../serialization/index.html[Boost.Serialization^] 进行归档/恢复。支持常规归档和 XML 归档。 + +==== 将 unordered_flat_set 保存到归档中 + +将 `unordered_flat_set` `x` 的所有元素保存到归档(XML 归档)`ar` 中。 + +[horizontal] +要求:;; `value_type` 必须是可序列化的(支持 XML 序列化),并且支持 Boost.Serialization 的 `save_construct_data`/`load_construct_data` 协议(该协议由 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^] 类型的自动支持)。 + +--- + +==== 从归档中加载 unordered_flat_set + +删除 `unordered_flat_set` `x` 中所有已存在的元素,并从归档(XML 归档)`ar` 中插入由 `ar` 所读取存储中保存的原始 `unordered_flat_set` `other` 元素的恢复副本。 + +[horizontal] +要求:;; `x.key_equal()` 在功能上等价于 `other.key_equal()`。 + +--- + +==== 将迭代器/常量迭代器保存到归档 + +将 `iterator`(`const_iterator`)`it` 的位置信息保存到归档(XML 归档)`ar` 中。`it` 可以是 `end()` 迭代器。 + +[horizontal] +要求:;; `it` 所指向的 `unordered_flat_set` `x` 此前已保存至 `ar`,并且在保存 `x` 和保存 `it` 之间未对 `x` 执行任何修改操作。 + +--- + +==== 从归档加载迭代器/常量迭代器 + +使 `iterator`(`const_iterator`)`it` 指向保存到由归档(XML 归档)`ar` 读取的存储中的原始 `iterator`(`const_iterator`)的被恢复位置。 + +[horizontal] +要求:;; 如果 `x` 是 `it` 所指向的 `unordered_flat_set`,则在加载 `x` 和加载 `it` 之间未对 `x` 执行任何修改操作。 diff --git a/doc/modules/ROOT/pages/reference/unordered_map_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/unordered_map_zh_Hans.adoc new file mode 100644 index 0000000..066cf79 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/unordered_map_zh_Hans.adoc @@ -0,0 +1,1477 @@ +[#unordered_map] +== 类模板 unordered_map + +:idprefix: unordered_map_ + +`boost::unordered_map` — 一个无序关联容器,用于将唯一的键与另一个值关联起来。 + +=== 概要 + +[listing,subs="+macros,+quotes"] +----- +// #include xref:reference/header_unordered_map.adoc[] + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + class unordered_map { + public: + // types + using key_type = Key; + using mapped_type = T; + using value_type = std::pair; + using hasher = Hash; + using key_equal = Pred; + using allocator_type = Allocator; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + using iterator = _implementation-defined_; + using const_iterator = _implementation-defined_; + using local_iterator = _implementation-defined_; + using const_local_iterator = _implementation-defined_; + using node_type = _implementation-defined_; + using insert_return_type = _implementation-defined_; + + // construct/copy/destroy + xref:#unordered_map_default_constructor[unordered_map](); + explicit xref:#unordered_map_bucket_count_constructor[unordered_map](size_type n, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + template + xref:#unordered_map_iterator_range_constructor[unordered_map](InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#unordered_map_copy_constructor[unordered_map](const unordered_map& other); + xref:#unordered_map_move_constructor[unordered_map](unordered_map&& other); + template + xref:#unordered_map_iterator_range_constructor_with_allocator[unordered_map](InputIterator f, InputIterator l, const allocator_type& a); + explicit xref:#unordered_map_allocator_constructor[unordered_map](const Allocator& a); + xref:#unordered_map_copy_constructor_with_allocator[unordered_map](const unordered_map& other, const Allocator& a); + xref:#unordered_map_move_constructor_with_allocator[unordered_map](unordered_map&& other, const Allocator& a); + xref:#unordered_map_initializer_list_constructor[unordered_map](std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#unordered_map_bucket_count_constructor_with_allocator[unordered_map](size_type n, const allocator_type& a); + xref:#unordered_map_bucket_count_constructor_with_hasher_and_allocator[unordered_map](size_type n, const hasher& hf, const allocator_type& a); + template + xref:#unordered_map_iterator_range_constructor_with_bucket_count_and_allocator[unordered_map](InputIterator f, InputIterator l, size_type n, const allocator_type& a); + template + xref:#unordered_map_iterator_range_constructor_with_bucket_count_and_hasher[unordered_map](InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); + xref:#unordered_map_initializer_list_constructor_with_allocator[unordered_map](std::initializer_list il, const allocator_type& a); + xref:#unordered_map_initializer_list_constructor_with_bucket_count_and_allocator[unordered_map](std::initializer_list il, size_type n, const allocator_type& a); + xref:#unordered_map_initializer_list_constructor_with_bucket_count_and_hasher_and_allocator[unordered_map](std::initializer_list il, size_type n, const hasher& hf, + const allocator_type& a); + xref:#unordered_map_destructor[~unordered_map](); + unordered_map& xref:#unordered_map_copy_assignment[operator++=++](const unordered_map& other); + unordered_map& xref:#unordered_map_move_assignment[operator++=++](unordered_map&& other) + noexcept(boost::allocator_traits::is_always_equal::value && + boost::is_nothrow_move_assignable_v && + boost::is_nothrow_move_assignable_v); + unordered_map& xref:#unordered_map_initializer_list_assignment[operator++=++](std::initializer_list); + allocator_type xref:#unordered_map_get_allocator[get_allocator]() const noexcept; + + // iterators + iterator xref:#unordered_map_begin[begin]() noexcept; + const_iterator xref:#unordered_map_begin[begin]() const noexcept; + iterator xref:#unordered_map_end[end]() noexcept; + const_iterator xref:#unordered_map_end[end]() const noexcept; + const_iterator xref:#unordered_map_cbegin[cbegin]() const noexcept; + const_iterator xref:#unordered_map_cend[cend]() const noexcept; + + // capacity + ++[[nodiscard]]++ bool xref:#unordered_map_empty[empty]() const noexcept; + size_type xref:#unordered_map_size[size]() const noexcept; + size_type xref:#unordered_map_max_size[max_size]() const noexcept; + + // modifiers + template std::pair xref:#unordered_map_emplace[emplace](Args&&... args); + template iterator xref:#unordered_map_emplace_hint[emplace_hint](const_iterator position, Args&&... args); + std::pair xref:#unordered_map_copy_insert[insert](const value_type& obj); + std::pair xref:#unordered_map_move_insert[insert](value_type&& obj); + template std::pair xref:#unordered_map_emplace_insert[insert](P&& obj); + iterator xref:#unordered_map_copy_insert_with_hint[insert](const_iterator hint, const value_type& obj); + iterator xref:#unordered_map_move_insert_with_hint[insert](const_iterator hint, value_type&& obj); + template iterator xref:#unordered_map_emplace_insert_with_hint[insert](const_iterator hint, P&& obj); + template void xref:#unordered_map_insert_iterator_range[insert](InputIterator first, InputIterator last); + void xref:#unordered_map_insert_initializer_list[insert](std::initializer_list); + + template + std::pair xref:#unordered_map_try_emplace[try_emplace](const key_type& k, Args&&... args); + template + std::pair xref:#unordered_map_try_emplace[try_emplace](key_type&& k, Args&&... args); + template + std::pair xref:#unordered_map_try_emplace[try_emplace](K&& k, Args&&... args); + template + iterator xref:#unordered_map_try_emplace_with_hint[try_emplace](const_iterator hint, const key_type& k, Args&&... args); + template + iterator xref:#unordered_map_try_emplace_with_hint[try_emplace](const_iterator hint, key_type&& k, Args&&... args); + template + iterator xref:#unordered_map_try_emplace_with_hint[try_emplace](const_iterator hint, K&& k, Args&&... args); + template + std::pair xref:#unordered_map_insert_or_assign[insert_or_assign](const key_type& k, M&& obj); + template + std::pair xref:#unordered_map_insert_or_assign[insert_or_assign](key_type&& k, M&& obj); + template + std::pair xref:#unordered_map_insert_or_assign[insert_or_assign](K&& k, M&& obj); + template + iterator xref:#unordered_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, const key_type& k, M&& obj); + template + iterator xref:#unordered_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, key_type&& k, M&& obj); + template + iterator xref:#unordered_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, K&& k, M&& obj); + + node_type xref:#unordered_map_extract_by_iterator[extract](const_iterator position); + node_type xref:#unordered_map_extract_by_key[extract](const key_type& k); + template node_type xref:#unordered_map_extract_by_key[extract](K&& k); + insert_return_type xref:#unordered_map_insert_with_node_handle[insert](node_type&& nh); + iterator xref:#unordered_map_insert_with_hint_and_node_handle[insert](const_iterator hint, node_type&& nh); + + iterator xref:#unordered_map_erase_by_position[erase](iterator position); + iterator xref:#unordered_map_erase_by_position[erase](const_iterator position); + size_type xref:#unordered_map_erase_by_key[erase](const key_type& k); + template size_type xref:#unordered_map_erase_by_key[erase](K&& k); + iterator xref:#unordered_map_erase_range[erase](const_iterator first, const_iterator last); + void xref:#unordered_map_quick_erase[quick_erase](const_iterator position); + void xref:#unordered_map_erase_return_void[erase_return_void](const_iterator position); + void xref:#unordered_map_swap[swap](unordered_map& other) + noexcept(boost::allocator_traits::is_always_equal::value && + boost::is_nothrow_swappable_v && + boost::is_nothrow_swappable_v); + void xref:#unordered_map_clear[clear]() noexcept; + + template + void xref:#unordered_map_merge[merge](unordered_map& source); + template + void xref:#unordered_map_merge[merge](unordered_map&& source); + template + void xref:#unordered_map_merge[merge](unordered_multimap& source); + template + void xref:#unordered_map_merge[merge](unordered_multimap&& source); + + // observers + hasher xref:#unordered_map_hash_function[hash_function]() const; + key_equal xref:#unordered_map_key_eq[key_eq]() const; + + // map operations + iterator xref:#unordered_map_find[find](const key_type& k); + const_iterator xref:#unordered_map_find[find](const key_type& k) const; + template + iterator xref:#unordered_map_find[find](const K& k); + template + const_iterator xref:#unordered_map_find[find](const K& k) const; + template + iterator xref:#unordered_map_find[find](CompatibleKey const& k, CompatibleHash const& hash, + CompatiblePredicate const& eq); + template + const_iterator xref:#unordered_map_find[find](CompatibleKey const& k, CompatibleHash const& hash, + CompatiblePredicate const& eq) const; + size_type xref:#unordered_map_count[count](const key_type& k) const; + template + size_type xref:#unordered_map_count[count](const K& k) const; + bool xref:#unordered_map_contains[contains](const key_type& k) const; + template + bool xref:#unordered_map_contains[contains](const K& k) const; + std::pair xref:#unordered_map_equal_range[equal_range](const key_type& k); + std::pair xref:#unordered_map_equal_range[equal_range](const key_type& k) const; + template + std::pair xref:#unordered_map_equal_range[equal_range](const K& k); + template + std::pair xref:#unordered_map_equal_range[equal_range](const K& k) const; + + // element access + mapped_type& xref:#unordered_map_operator[operator[+]+](const key_type& k); + mapped_type& xref:#unordered_map_operator[operator[+]+](key_type&& k); + template mapped_type& xref:#unordered_map_operator[operator[+]+](K&& k); + mapped_type& xref:#unordered_map_at[at](const key_type& k); + const mapped_type& xref:#unordered_map_at[at](const key_type& k) const; + template mapped_type& xref:#unordered_map_at[at](const K& k); + template const mapped_type& xref:#unordered_map_at[at](const K& k) const; + + // bucket interface + size_type xref:#unordered_map_bucket_count[bucket_count]() const noexcept; + size_type xref:#unordered_map_max_bucket_count[max_bucket_count]() const noexcept; + size_type xref:#unordered_map_bucket_size[bucket_size](size_type n) const; + size_type xref:#unordered_map_bucket[bucket](const key_type& k) const; + template size_type xref:#unordered_map_bucket[bucket](const K& k) const; + local_iterator xref:#unordered_map_begin_2[begin](size_type n); + const_local_iterator xref:#unordered_map_begin_2[begin](size_type n) const; + local_iterator xref:#unordered_map_end_2[end](size_type n); + const_local_iterator xref:#unordered_map_end_2[end](size_type n) const; + const_local_iterator xref:#unordered_map_cbegin_2[cbegin](size_type n) const; + const_local_iterator xref:#unordered_map_cend_2[cend](size_type n) const; + + // hash policy + float xref:#unordered_map_load_factor[load_factor]() const noexcept; + float xref:#unordered_map_max_load_factor[max_load_factor]() const noexcept; + void xref:#unordered_map_set_max_load_factor[max_load_factor](float z); + void xref:#unordered_map_rehash[rehash](size_type n); + void xref:#unordered_map_reserve[reserve](size_type n); + }; + + // Deduction Guides + template>, + class Pred = std::equal_to>, + class Allocator = std::allocator>> + unordered_map(InputIterator, InputIterator, typename xref:#unordered_map_deduction_guides[__see below__]::size_type = xref:#unordered_map_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_map, xref:#unordered_map_iter_mapped_type[__iter-mapped-type__], Hash, Pred, + Allocator>; + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + unordered_map(std::initializer_list>, + typename xref:#unordered_map_deduction_guides[__see below__]::size_type = xref:#unordered_map_deduction_guides[__see below__], Hash = Hash(), + Pred = Pred(), Allocator = Allocator()) + -> unordered_map; + + template + unordered_map(InputIterator, InputIterator, typename xref:#unordered_map_deduction_guides[__see below__]::size_type, Allocator) + -> unordered_map, xref:#unordered_map_iter_mapped_type[__iter-mapped-type__], + boost::hash>, + std::equal_to>, Allocator>; + + template + unordered_map(InputIterator, InputIterator, Allocator) + -> unordered_map, xref:#unordered_map_iter_mapped_type[__iter-mapped-type__], + boost::hash>, + std::equal_to>, Allocator>; + + template + unordered_map(InputIterator, InputIterator, typename xref:#unordered_map_deduction_guides[__see below__]::size_type, Hash, Allocator) + -> unordered_map, xref:#unordered_map_iter_mapped_type[__iter-mapped-type__], Hash, + std::equal_to>, Allocator>; + + template + unordered_map(std::initializer_list>, typename xref:#unordered_map_deduction_guides[__see below__]::size_type, + Allocator) + -> unordered_map, std::equal_to, Allocator>; + + template + unordered_map(std::initializer_list>, Allocator) + -> unordered_map, std::equal_to, Allocator>; + + template + unordered_map(std::initializer_list>, typename xref:#unordered_map_deduction_guides[__see below__]::size_type, Hash, + Allocator) + -> unordered_map, Allocator>; + +} // namespace unordered +} // namespace boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_Key_ +|`Key` must be https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the container (i.e. `allocator_traits` can destroy it). + +|_T_ +|`T` must be https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the container (i.e. `allocator_traits` can destroy it). + +|_Hash_ +|A unary function object type that acts a hash function for a `Key`. It takes a single argument of type `Key` and returns a value of type `std::size_t`. + +|_Pred_ +|A binary function object that implements an equivalence relation on values of type `Key`. A binary function object that induces an equivalence relation on values of type `Key`. It takes two arguments of type `Key` and returns a value of type bool. + +|_Allocator_ +|An allocator whose value type is the same as the container's value type. +支持使https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[异形指针] 的分配器。 + +|=== + +元素被组织到桶中。具有相同哈希码的键存储在同一桶中。 + +桶的数量可以通过调用 insert 自动增加,或者作为调用 rehash 的结果。 + +=== 配置宏 + +==== `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0` + +全局定义此宏以支持加载由 Boost 1.84 之前版本的 Boost 保存到 Boost.Serialization 归档中的 `unordered_map`。 + +=== 类型定义 + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ iterator; +---- + +一个迭代器,其值类型为 `value_type`。 + +迭代器类别至少为前向迭代器。 + +可转换为 `const_iterator` 。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ const_iterator; +---- + +一个常量迭代器,其值类型为 `value_type` 。 + +迭代器类别至少为前向迭代器。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ local_iterator; +---- + +一种迭代器,其值类型、差值类型以及指针和引用类型均与 iterator 相同。 + +`local_iterator` 对象可用于遍历单个桶内的元素。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ const_local_iterator; +---- + +一种常量迭代器,其值类型、差值类型以及指针和引用类型均与 const_iterator 相同。 + +`const_local_iterator` 对象可用于遍历单个桶。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ node_type; +---- + +一个用于存放被提取容器元素的类,符合 https://en.cppreference.com/w/cpp/container/node_handle[NodeHandle] 模型。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ insert_return_type; +---- + +一个内部类模板的特化: + +[source,c++,subs=+quotes] +---- +template +struct _insert_return_type_ // name is exposition only +{ + Iterator position; + bool inserted; + NodeType node; +}; +---- + +其中 `Iterator` = `iterator`,`NodeType` = `node_type`。 + +--- + +=== 构造函数 + +==== 默认构造函数 +```c++ unordered_map(); ``` + +使用 `hasher()` 作为哈希函数,`key_equal()` 作为键相等谓词,`allocator_type()` 作为分配器,以及最大负载因子 `1.0` 构造一个空容器。 + +[horizontal] +后置条件:`size() == 0` +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 桶数构造函数 +```c++ explicit unordered_map(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,以及最大负载因子 `1.0`。 + +[horizontal] +后置条件:`size() == 0` +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_map(InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,以及最大负载因子 `1.0`,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 复制构造函数 +```c++ unordered_map(unordered_map const& other); ``` + +拷贝构造函数。拷贝所包含的元素、哈希函数、谓词、最大负载因子和分配器。 + +如果 `Allocator::select_on_container_copy_construction` 存在且具有正确的签名,则分配器将根据其结果进行构造。 + +[horizontal] +要求:`value_type` 可拷贝构造。 + +--- + +==== 移动构造函数 +```c++ unordered_map(unordered_map&& other); ``` + +移动构造函数。 + +[horizontal] +注意:此函数使用 Boost.Move 实现。 +要求:`value_type` 可移动构造。 + +--- + +==== 带分配器的迭代器区间构造函数 +```c++ template unordered_map(InputIterator f, InputIterator l, const allocator_type& a); ``` + +使用 `a` 作为分配器,以默认哈希函数、默认键相等谓词和最大负载因子 `1.0` 构造一个空容器,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:`hasher`、`key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 分配器构造函数 +```c++ explicit unordered_map(Allocator const& a); ``` + +使用分配器 `a` 构造一个空容器。 + +--- + +==== 带分配器的复制构造函数 +```c++ unordered_map(unordered_map const& other, Allocator const& a); ``` + +构造一个容器,拷贝 `other` 所包含的元素、哈希函数、谓词和最大负载因子,但使用分配器 `a`。 + +--- + +==== 带分配器的移动构造函数 +```c++ unordered_map(unordered_map&& other, Allocator const& a); ``` + +构造一个容器,移动 other 所包含的元素,并使用其哈希函数、谓词和最大负载因子,但使用分配器 a。 + +[horizontal] +注意:此函数使用 Boost.Move 实现。 要求:`value_type` 可移动插入。 + +--- + +==== 初始化列表构造函数 +[source,c++,subs="+quotes"] +---- +unordered_map(std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 n 个桶的空容器,使用 hf 作为哈希函数,eql 作为键相等谓词,a 作为分配器,以及最大负载因子 1.0,并将 il 中的元素插入其中。 + +[horizontal] +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带分配器的桶数构造函数 +```c++ unordered_map(size_type n, allocator_type const& a); ``` + +构造一个至少包含 n 个桶的空容器,使用 hf 作为哈希函数,使用默认的哈希函数和键相等谓词,a 作为分配器,以及最大负载因子 1.0。 + +[horizontal] +后置条件:`size() == 0` +要求:`hasher` 和 `key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带哈希函数和分配器的桶数构造函数 +```c++ unordered_map(size_type n, hasher const& hf, allocator_type const& a); ``` + +构造一个至少包含 n 个桶的空容器,使用 hf 作为哈希函数,使用默认的键相等谓词,a 作为分配器,以及最大负载因子 1.0。 + +[horizontal] +后置条件:`size() == 0` 要求:`key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数和分配器的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 作为分配器,使用默认的哈希函数、默认的键相等谓词以及最大负载因子 `1.0`,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:`hasher`、`key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数和哈希函数的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- + template + unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`a` 作为分配器,使用默认的键相等谓词,以及最大负载因子 `1.0`,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:`key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带分配器的初始化列表构造函数 + +```c++ unordered_map(std::initializer_list il, const allocator_type& a); ``` + +使用 a 作为分配器,最大负载因子为 1.0,构造一个空容器,并将 il 中的元素插入其中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数和分配器的初始化列表构造函数 + +```c++ unordered_map(std::initializer_list il, size_type n, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 作为分配器,最大负载因子为 `1.0`,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数、哈希函数和分配器的初始化列表构造函数 + +```c++ unordered_map(std::initializer_list il, size_type n, const hasher& hf, const allocator_type& a); ``` + +构造一个至少包含 n 个桶的空容器,使用 hf 作为哈希函数,a 作为分配器,最大负载因子为 1.0,并将 il 中的元素插入其中。 + +[horizontal] +要求:`key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +=== 析构函数 + +```c++ ~unordered_map(); ``` + +[horizontal] +注意:析构函数会应用于每个元素,并且所有内存都会被释放。 + +--- + +=== 赋值操作 + +==== 复制赋值 + +```c++ unordered_map& operator=(unordered_map const& other); ``` + +赋值运算符。拷贝所包含的元素、哈希函数、谓词和最大负载因子,但不拷贝分配器。 + +如果 `Alloc::propagate_on_container_copy_assignment` 存在且 `Alloc::propagate_on_container_copy_assignment::value` 为 `true`,则分配器会被覆盖;否则,拷贝的元素将使用现有的分配器创建。 + +[horizontal] +要求:`value_type` 可拷贝构造。 + +--- + +==== 移动赋值 +```c++ unordered_map& operator=(unordered_map&& other) noexcept(boost::allocator_traits::is_always_equal::value && boost::is_nothrow_move_assignable_v && boost::is_nothrow_move_assignable_v); ``` The move assignment operator. + +如果 `Alloc::propagate_on_container_move_assignment` 存在且 `Alloc::propagate_on_container_move_assignment::value` 为 `true`,则分配器会被覆盖;否则,移动的元素将使用现有的分配器创建。 + +[horizontal] +要求:`value_type` 可移动构造。 + +--- + +==== 初始化列表赋值 +```c++ unordered_map& operator=(std::initializer_list il); ``` + +从初始化列表中的值进行赋值。所有现有元素要么被新元素覆盖,要么被销毁。 + +[horizontal] +要求:`value_type` 必须能够从容器中 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^] 并且满足 https://en.cppreference.com/w/cpp/named_req/CopyAssignable[可拷贝赋值^]。 + +=== 迭代器 + +==== begin +```c++ iterator begin() noexcept; const_iterator begin() const noexcept; ``` + +[horizontal] +返回:指向容器第一个元素的迭代器,如果容器为空则返回容器的尾后迭代器。 + +--- + +==== end +```c++ iterator end() noexcept; const_iterator end() const noexcept; ``` + +[horizontal] +返回:指向容器尾后值的迭代器。 + +--- + +==== cbegin +```c++ const_iterator cbegin() const noexcept; ``` + +[horizontal] +返回:指向容器第一个元素的 `const_iterator`,如果容器为空则返回容器的尾后值。 + +--- + +==== cend +```c++ const_iterator cend() const noexcept; ``` + +[horizontal] +返回:指向容器尾后值的 `const_iterator`。 + +--- + +=== 大小与容量 + +==== 空 + +```c++ [[nodiscard]] bool empty() const noexcept; ``` + +[horizontal] +返回:`size() == 0`。 + +--- + +==== 大小 + +```c++ size_type size() const noexcept; ``` + +[horizontal] +返回:`std::distance(begin(), end())`。 + +--- + +==== max_size + +```c++ size_type max_size() const noexcept; ``` + +[horizontal] +返回:可能的最大容器的 `size()`。 + +--- + +=== 修改器 + +==== 原地构造 +```c++ template std::pair emplace(Args&&... args); ``` + +当且仅当容器中没有具有等价键的元素时,才插入一个使用参数 `args` 构造的对象。 + +[horizontal] +要求:`value_type` 必须可以从 `args` 出发在 `X` 中进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^]。 返回:如果发生了插入,则返回类型的 bool 分量为 true。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 指向元素的指针和引用永远不会失效。 +如果 `args...` 的形式为 `k,v`,则该函数会延迟构造整个对象,直到确定应该插入元素为止,仅使用 `k` 参数进行检查。当映射的 `key_type` 可移动构造或 `k` 参数本身就是 `key_type` 类型时,此优化生效。 + +--- + +==== emplace_hint +```c++ template iterator emplace_hint(const_iterator position, Args&&... args); ``` + +当且仅当容器中没有具有等价键的元素时,才插入一个使用参数 `args` 构造的对象。 + +`position` 是关于元素插入位置的建议。 + +[horizontal] +要求:`value_type` 必须可以从 `args` 出发在 `X` 中进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^]。 返回:如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:标准关于 `hint` 的含义表述相当模糊。但唯一实际的使用方式,也是 Boost.Unordered 支持的唯一方式,是将其指向一个具有相同键的已存在元素。 +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 +如果 `args...` 的形式为 `k,v`,则该函数会延迟构造整个对象,直到确定应该插入元素为止,仅使用 `k` 参数进行检查。当映射的 `key_type` 可移动构造或 `k` 参数本身就是 `key_type` 类型时,此优化生效。 + +--- + +==== 复制插入 +```c++ std::pair insert(const value_type& obj); ``` + +当且仅当容器中没有具有等价键的元素时,才将 `obj` 插入容器。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 +返回:返回类型的 bool 分量为 true 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 移动插入 +```c++ std::pair insert(value_type&& obj); ``` + +当且仅当容器中没有具有等价键的元素时,才将 `obj` 插入容器。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^]。 +返回:返回类型的 bool 分量为 true 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 原地插入 +```c++ template std::pair insert(P&& obj); ``` + +通过执行 `emplace(std::forward

(value))` 向容器中插入一个元素。

+ +仅当 std::is_constructible::value 为 true 时参与重载决议。 + +[horizontal] +返回:返回类型的 bool 分量为 true 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 + +--- + +==== 带提示的复制插入 +```c++iterator insert(const_iterator hint, const value_type& obj);``` +当且仅当容器中没有具有等价键的元素时,才将 `obj` 插入容器。 + +`hint` 是关于元素插入位置的建议。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 +返回:如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:标准关于 `hint` 的含义表述相当模糊。但唯一实际的使用方式,也是 Boost.Unordered 支持的唯一方式,是将其指向一个具有相同键的已存在元素。 +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 带提示的移动插入 +```c++ iterator insert(const_iterator hint, value_type&& obj); ``` + +当且仅当容器中没有具有等价键的元素时,才将 `obj` 插入容器。 + +`hint` 是关于元素插入位置的建议。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^]。 +返回:如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:标准关于 `hint` 的含义表述相当模糊。但唯一实际的使用方式,也是 Boost.Unordered 支持的唯一方式,是将其指向一个具有相同键的已存在元素。 +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 带提示的原地插入 + +```c++ template iterator insert(const_iterator hint, P&& obj); ``` + +通过执行 `emplace_hint(hint, std::forward

(value))` 向容器中插入一个元素。

+ +仅当 std::is_constructible::value 为 true 时参与重载决议。 + +`hint` 是关于元素插入位置的建议。 + +[horizontal] +返回:如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +注意:标准关于 `hint` 的含义表述相当模糊。但唯一实际的使用方式,也是 Boost.Unordered 支持的唯一方式,是将其指向一个具有相同键的已存在元素。 +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 迭代器范围插入 +```c++ template void insert(InputIterator first, InputIterator last); ``` + +将一个范围内的元素插入容器中。当且仅当容器中没有具有等价键的元素时,才会插入这些元素。 + +[horizontal] +要求:`value_type` 必须可以从 `*first` 出发在 `X` 中进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^]。 +抛出:当插入单个元素时,如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 初始化列表插入 +```c++ void insert(std::initializer_list); ``` + +将一个范围内的元素插入容器中。当且仅当容器中没有具有等价键的元素时,才会插入这些元素。 + +[horizontal] +要求:`value_type` 可以在容器中进行 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 +抛出:当插入单个元素时,如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== try_emplace +```c++ template std::pair try_emplace(const key_type& k, Args&&... args); template std::pair try_emplace(key_type&& k, Args&&... args); template std::pair try_emplace(K&& k, Args&&... args) ``` + +如果容器中不存在键为 `k` 的元素,则插入一个新元素。 + +如果已存在键为 `k` 的元素,则该函数不执行任何操作。 + +[horizontal] +返回:返回类型的 bool 分量为 true 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:此函数类似于 xref:#unordered_map_emplace[emplace],区别在于 `value_type` 的构造方式如下: ```c++ +// first two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) + +// third overload +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) ``` + +而不是像 xref:#unordered_map_emplace[emplace] 那样简单地将所有参数转发给 `value_type` 的构造函数。 + +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 + +指向元素的指针和引用永远不会失效。 + +`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 都不能从 `K` 隐式转换时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 带提示位置的 `try_emplace` +```c++ template iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); template iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); template iterator try_emplace(const_iterator hint, K&& k, Args&&... args); ``` + +如果容器中不存在键为 `k` 的元素,则插入一个新元素。 + +如果已存在键为 `k` 的元素,则该函数不执行任何操作。 + +`hint` 是关于元素插入位置的建议。 + +[horizontal] +返回:如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:此函数类似于 xref:#unordered_map_emplace_hint[emplace_hint],区别在于 `value_type` 的构造方式如下: +```c++ +// first two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) + +// third overload +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) ``` + +而不是像 xref:#unordered_map_emplace_hint[emplace_hint] 那样简单地将所有参数转发给 `value_type` 的构造函数。 + +标准关于 `hint` 的含义表述相当模糊。但唯一实际的使用方式,也是 Boost.Unordered 支持的唯一方式,是将其指向一个具有相同键的已存在元素。 + +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 + +指向元素的指针和引用永远不会失效。 + +`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 都不能从 `K` 隐式转换时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== insert_or_assign +```c++ template std::pair insert_or_assign(const key_type& k, M&& obj); template std::pair insert_or_assign(key_type&& k, M&& obj); template std::pair insert_or_assign(K&& k, M&& obj); ``` + +向容器中插入一个新元素,或通过赋值更新现有元素的值。 + +如果存在键为 `k` 的元素,则通过赋值 `std::forward(obj)` 来更新它。 + +如果不存在这样的元素,则将其添加到容器中,形式为: +```c++ +// first two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(obj))) + +// third overload +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(obj))) ``` + +[horizontal] +返回:返回类型的 bool 分量为 true 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +`template` 仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 带提示位置的 `insert_or_assign` +```c++ template iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); template iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); template iterator insert_or_assign(const_iterator hint, K&& k, M&& obj); ``` + +向容器中插入一个新元素,或通过赋值更新现有元素的值。 + +如果存在键为 `k` 的元素,则通过赋值 `std::forward(obj)` 来更新它。 + +如果不存在这样的元素,则将其添加到容器中,形式为: +```c++ +// first two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(obj))) + +// third overload +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(obj))) ``` + +`hint` 是关于元素插入位置的建议。 + +[horizontal] +返回:如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:标准关于 `hint` 的含义表述相当模糊。但唯一实际的使用方式,也是 Boost.Unordered 支持的唯一方式,是将其指向一个具有相同键的已存在元素。 +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 指向元素的指针和引用永远不会失效。 +`template` 仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 通过迭代器提取 +```c++ node_type extract(const_iterator position); ``` + +移除 `position` 所指向的元素。 + +[horizontal] +返回:一个拥有该元素的 `node_type`。 +注意:通过此方法提取的节点可以插入到兼容的 `unordered_multimap` 中。 + +--- + +==== 通过键提取 +```c++ node_type extract(const key_type& k); template node_type extract(K&& k); ``` + +移除一个键与 `k` 等价的元素。 + +[horizontal] +返回:如果找到该元素,则返回一个拥有该元素的 `node_type`;否则返回一个空的 `node_type`。 +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +注意:通过此方法提取的节点可以插入到兼容的 `unordered_multimap` 中。 +`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 都不能从 `K` 隐式转换时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 通过 `node_handle` 插入 +```c++ insert_return_type insert(node_type&& nh); ``` + +如果 `nh` 为空,则无效果。 + +否则,当且仅当容器中没有具有等价键的元素时,才插入 `nh` 所拥有的元素。 + +[horizontal] +要求:`nh` 为空或 `nh.get_allocator()` 等于容器的分配器。 +返回:如果 `nh` 为空,则返回一个 `insert_return_type`,其中 `inserted` 为 `false`,`position` 等于 `end()`,`node` 为空。 +否则如果已存在具有等价键的元素,则返回一个 `insert_return_type`,其中 `inserted` 为 `false`,`position` 指向匹配的元素,`node` 包含 `nh` 中的节点。 +否则如果插入成功,则返回一个 `insert_return_type`,其中 `inserted` 为 `true`,`position` 指向新插入的元素,`node` 为空。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 +此函数可用于插入从兼容的 `unordered_multimap` 中提取的节点。 + +--- + +==== 带提示和 `node_handle` 的插入 +```c++ iterator insert(const_iterator hint, node_type&& nh); ``` + +如果 `nh` 为空,则无效果。 + +否则,当且仅当容器中没有具有等价键的元素时,才插入 `nh` 所拥有的元素。 + +如果容器中已存在具有等价键的元素,则对 `nh` 无影响(即 `nh` 仍然包含该节点)。 + +`hint` 是关于元素插入位置的建议。 + +[horizontal] +要求:`nh` 为空或 `nh.get_allocator()` 等于容器的分配器。 +返回:如果 `nh` 为空,则返回 `end()`。 +如果容器中已存在具有等价键的元素,则返回指向该元素的迭代器。 +否则返回指向新插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:标准关于 `hint` 的含义表述相当模糊。但唯一实际的使用方式,也是 Boost.Unordered 支持的唯一方式,是将其指向一个具有相同键的已存在元素。 +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 +此函数可用于插入从兼容的 `unordered_multimap` 中提取的节点。 + +--- + +==== 通过位置擦除 + +```c++ iterator erase(iterator position); iterator erase(const_iterator position); ``` + +擦除 `position` 所指向的元素。 + +[horizontal] +返回:`position` 在被擦除之前的后一个迭代器。 +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +注意:在旧版本中,此操作可能效率较低,因为它需要搜索多个桶以找到返回迭代器的位置。数据结构已经更改,不再是这种情况,并且备用的擦除方法已被弃用。 + +--- + +==== 通过键擦除 +```c++ size_type erase(const key_type& k); template size_type erase(K&& k); ``` + +擦除所有键与 `k` 等价的元素。 + +[horizontal] +返回:被擦除的元素数量。 +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 都不能从 `K` 隐式转换时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 范围擦除 + +```c++ iterator erase(const_iterator first, const_iterator last); ``` + +擦除从 `first` 到 `last` 范围内的元素。 + +[horizontal] +返回:被擦除元素之后的迭代器——即 `last`。 +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +在此实现中,此重载不会调用任何函数对象的方法,因此不会抛出异常,但这在其他实现中可能并不成立。 + +--- + +==== quick_erase +```c++ void quick_erase(const_iterator position); ``` + +擦除 `position` 所指向的元素。 + +[horizontal] +抛出:;; 仅当由 `hasher` 或 `key_equal` 抛出异常时才会抛出异常。 + +在此实现中,此重载不会调用任一函数对象的方法,因此不会抛出异常,但在其他实现中可能并非如此。 + +备注:;; 之所以实现此方法,是因为从 `erase` 返回下一个元素的迭代器开销较大,但容器已经过重新设计,情况不再如此。因此,此方法现已废弃。 + +--- + +==== erase_return_void +```c++ void erase_return_void(const_iterator position); ``` + +擦除 `position` 所指向的元素。 + +[horizontal] +抛出:;; 仅当由 `hasher` 或 `key_equal` 抛出异常时才会抛出异常。 + +在此实现中,此重载不会调用任一函数对象的方法,因此不会抛出异常,但在其他实现中可能并非如此。 + +备注:;; 之所以实现此方法,是因为从 `erase` 返回下一个元素的迭代器开销较大,但容器已经过重新设计,情况不再如此。因此,此方法现已废弃。 + +--- + +==== 交换 +```c++ void swap(unordered_map& other) noexcept(boost::allocator_traits::is_always_equal::value && boost::is_nothrow_swappable_v && boost::is_nothrow_swappable_v); ``` + +将容器的内容与参数进行交换。 + +如果 `Allocator::propagate_on_container_swap` 已声明且 `Allocator::propagate_on_container_swap::value` 为 `true`,则交换两个容器的分配器。否则,使用不相等的分配器进行交换将导致未定义行为。 + +[horizontal] +抛出:;; 除非由 `key_equal` 或 `hasher` 的复制构造函数或复制赋值运算符抛出异常,否则不会抛出异常。 +备注:;; 异常规范与 C++11 标准不完全相同,因为相等谓词和哈希函数是通过它们的复制构造函数进行交换的。 + +--- + +==== 清空 +```c++ void clear(); ``` + +清除容器中的所有元素。 + +[horizontal] +后置条件:;; `size() == 0`抛出:;; 永不抛出异常。 + +--- + +==== 合并 +```c++ template void merge(unordered_map& source); template void merge(unordered_map&& source); template void merge(unordered_multimap& source); template void merge(unordered_multimap&& source); ``` + +尝试通过迭代 `source` 并提取 `source` 中不包含在 `*this` 内的任何节点,然后将其插入到 `*this` 中,从而实现两个容器的“合并”。 + +由于 `source` 可能具有不同的哈希函数和键相等谓词,因此 `source` 中每个节点的键都将使用 `this->hash_function()` 重新计算哈希值,然后根据需要再使用 `this->key_eq()` 进行比较。 + +如果 `this->get_allocator() != source.get_allocator()`,则此函数的行为未定义。 + +此函数不会复制或移动任何元素,而只是将节点从 `source` 重新定位到 `*this` 中。 + +[horizontal] +注意:;; + -- +* 指向被转移元素的指针和引用保持有效。 +* 使指向被转移元素的迭代器失效。 +* 使属于 `*this` 的迭代器失效。 +* `source` 中未被转移的元素的迭代器保持有效。 +-- + +--- + +=== 观察器 + +==== get_allocator +``` allocator_type get_allocator() const; ``` + +--- + +==== 哈希函数 +``` hasher hash_function() const; ``` + +[horizontal] +返回:;; 容器的哈希函数。 + +--- + +==== key_eq +``` key_equal key_eq() const; ``` + +[horizontal] +返回:;; 容器的键相等谓词。 + +--- + +=== 查找 + +==== find +```c++ iterator find(const key_type& k); const_iterator find(const key_type& k) const; template iterator find(const K& k); template const_iterator find(const K& k) const; template iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq); template const_iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const; + +``` + +[horizontal] +返回:;; 指向键与 `k` 等价之元素的迭代器;若无此元素,则返回 `b.end()`。 +备注:;; 包含 `CompatibleKey`、`CompatibleHash` 和 `CompatiblePredicate` 的模板重载属于非标准扩展,允许使用兼容的哈希函数和相等谓词来处理不同类型的键,从而避免昂贵的类型转换。通常不建议使用它们,而应使用 `K` 成员函数模板。 +此外,仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时,`template` 重载才会参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 类型调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型的开销。 + +--- + +==== count +```c++ size_type count(const key_type& k) const; template size_type count(const K& k) const; ``` + +[horizontal] +返回:;; 键与 `k` 等价的元素个数。 +备注:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时,`template` 重载才会参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 类型调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型的开销。 + +--- + +==== 包含 +```c++ bool contains(const key_type& k) const; template bool contains(const K& k) const; ``` + +[horizontal] +返回:;; 一个布尔值,指示容器中是否存在键等于 `key` 的元素。 +备注:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时,`template` 重载才会参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 类型调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型的开销。 + +--- + +==== equal_range +```c++ std::pair equal_range(const key_type& k); std::pair equal_range(const key_type& k) const; template std::pair equal_range(const K& k); template std::pair equal_range(const K& k) const; ``` + +[horizontal] +返回:;; 一个范围,包含所有键与 `k` 等价的元素。若容器中不包含任何此类元素,则返回 `std::make_pair(b.end(), b.end())`。 +备注:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时,`template` 重载才会参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 类型调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型的开销。 + +--- + +==== operator++[++++]++ +```c++ mapped_type& operator[](const key_type& k); mapped_type& operator[](key_type&& k); template mapped_type& operator[](K&& k); ``` + +[horizontal] +效果:;; 如果容器中尚不存在键与 `k` 等价的元素,则插入值 `std::pair(k, mapped_type())`。 +返回:;; 指向 `x.second` 的引用,其中 `x` 是容器中已存在的元素,或者是键与 `k` 等价的新插入元素。 +抛出:;; 如果除调用 `hasher` 之外的操作抛出异常,则该函数无效。 +备注:;; 可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +此外,指向元素的指针和引用永远不会失效。 +另外,仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时,`template` 重载才会参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 类型调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型的开销。 + +--- + +==== at +```c++ mapped_type& at(const key_type& k); const mapped_type& at(const key_type& k) const; template mapped_type& at(const K& k); template const mapped_type& at(const K& k) const; ``` + +[horizontal] +返回:;; 指向 `x.second` 的引用,其中 `x` 是键与 `k` 等价的(唯一)元素。 +抛出:;; 如果不存在这样的元素,则抛出 `std::out_of_range` 类型的异常对象。 +备注:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时,`template` 重载才会参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 类型调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型的开销。 + +--- + +=== 桶接口 + +==== bucket_count +```c++ size_type bucket_count() const noexcept; ``` + +[horizontal] +返回:;; 桶的数量。 + +--- + +==== max_bucket_count +```c++ size_type max_bucket_count() const noexcept; ``` + +[horizontal] +返回:;; 桶数量的上限。 + +--- + +==== 桶大小 +```c++ size_type bucket_size(size_type n) const; ``` + +[horizontal] +要求:;; `n < bucket_count()`返回:;; 桶 `n` 中的元素个数。 + +--- + +==== 桶 +```c++ size_type bucket(const key_type& k) const; template size_type bucket(const K& k) const; ``` + +[horizontal] +返回:;; 包含键 `k` 的元素的桶索引。 +后置条件:;; 返回值小于 `bucket_count()`。 +备注:;; 仅当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时,`template` 重载才会参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 类型调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型的开销。 + +--- + +==== begin + +```c++ local_iterator begin(size_type n); const_local_iterator begin(size_type n) const; ``` + +[horizontal] +要求:;; `n` 应在 `[0, bucket_count())` 范围内。 +返回:;; 指向索引为 `n` 的桶中第一个元素的局部迭代器。 + +--- + +==== end +```c++ local_iterator end(size_type n); const_local_iterator end(size_type n) const; ``` + +[horizontal] +要求:;; `n` 应在 `[0, bucket_count())` 范围内。 +返回:;; 指向索引为 `n` 的桶中“尾后”元素的局部迭代器。 + +--- + +==== cbegin +```c++ const_local_iterator cbegin(size_type n) const; ``` + +[horizontal] +要求:;; `n` 应在 `[0, bucket_count())` 范围内。 +返回:;; 指向索引为 `n` 的桶中第一个元素的常量局部迭代器。 + +--- + +==== cend +```c++ const_local_iterator cend(size_type n) const; ``` + +[horizontal] +要求:;; `n` 应在 `[0, bucket_count())` 范围内。 +返回:;; 指向索引为 `n` 的桶中“尾后”元素的常量局部迭代器。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ float load_factor() const noexcept; ``` + +[horizontal] +返回:;; 每个桶的平均元素个数。 + +--- + +==== max_load_factor + +```c++ float max_load_factor() const noexcept; ``` + +[horizontal] +返回:;; 返回当前的最大负载因子。 + +--- + +==== 设置最大负载因子 +```c++ void max_load_factor(float z); ``` + +[horizontal] +效果:;; 改变容器的最大负载因子,以 `z` 作为提示。 + +--- + + +==== 重哈希 +```c++ void rehash(size_type n); ``` + +改变桶的数量,使得至少包含 `n` 个桶,并且使得负载因子小于或等于最大负载因子。在适用的情况下,这将增大或缩小与容器相关联的 `bucket_count()`。 + +当 `size() == 0` 时,`rehash(0)` 将释放底层桶数组的内存。 + +使迭代器失效,并改变元素的顺序。指向元素的指针和引用不会失效。 + +[horizontal] +抛出:;; 如果抛出异常(除非是由容器的哈希函数或比较函数抛出),则该函数无效。 + +--- + +==== 保留 +```c++ void reserve(size_type n); ``` + +等价于 `a.rehash(ceil(n / a.max_load_factor()))`;如果 `n > 0` 且 `a.max_load_factor() == std::numeric_limits::infinity()`,则等价于 `a.rehash(1)`。 + +与 `rehash` 类似,此函数可用于增加或减少容器中桶的数量。 + +使迭代器失效,并改变元素的顺序。指向元素的指针和引用不会失效。 + +[horizontal] +抛出:;; 如果抛出异常(除非是由容器的哈希函数或比较函数抛出),则该函数无效。 + +=== 推导指引 +在以下任一条件成立时,推导指引将不参与重载决议: + +- 它具有 `InputIterator` 模板参数,并且为该参数推导出的类型不符合输入迭代器的要求。 +- 它具有 `Allocator` 模板参数,并且为该参数推导出的类型不符合分配器的要求。 +- 它具有 `Hash` 模板参数,并且为该参数推导出的是整数类型或符合分配器要求的类型。 +- 它具有 `Pred` 模板参数,并且为该参数推导出的是符合分配器要求的类型。 + +推导指引中的 `size_type` 参数类型指向由该推导指引所推导出的容器类型的 `size_type` 成员类型。其默认值与所选构造函数的默认值一致。 + +==== _iter-value-type_ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-value-type__ = + typename std::iterator_traits::value_type; // exposition only +----- + +==== __iter-key-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-key-type__ = std::remove_const_t< + std::tuple_element_t<0, xref:#unordered_map_iter_value_type[__iter-value-type__]>>; // exposition only +----- + +==== __iter-mapped-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-mapped-type__ = + std::tuple_element_t<1, xref:#unordered_map_iter_value_type[__iter-value-type__]>; // exposition only +----- + +==== __iter-to-alloc-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-to-alloc-type__ = std::pair< + std::add_const_t>>, + std::tuple_element_t<1, xref:#unordered_map_iter_value_type[__iter-value-type__]>>; // exposition only +----- + +=== 相等性比较 + +==== operator +```c++ template bool operator==(const unordered_map& x, const unordered_map& y); ``` + +如果 `x.size() == y.size()` 并且对于 `x` 中的每个元素,在 `y` 中都有一个具有相同键且值相等的元素(使用 `operator==` 比较值类型),则返回 `true`。 + +[horizontal] +备注:;; 如果两个容器不具有等价的相等谓词,则行为未定义。 + +--- + +==== operator! +```c++ template bool operator!=(const unordered_map& x, const unordered_map& y); ``` + +如果 `x.size() == y.size()` 并且对于 `x` 中的每个元素,在 `y` 中都有一个具有相同键且值相等的元素(使用 `operator==` 比较值类型),则返回 `false`。 + +[horizontal] +备注:;; 如果两个容器不具有等价的相等谓词,则行为未定义。 + +=== 交换 +```c++ template void swap(unordered_map& x, unordered_map& y) noexcept(noexcept(x.swap(y))); ``` + +交换 `x` 和 `y` 的内容。 + +如果 `Allocator::propagate_on_container_swap` 已声明且 `Allocator::propagate_on_container_swap::value` 为 `true`,则交换两个容器的分配器。否则,使用不相等的分配器进行交换将导致未定义行为。 + +[horizontal] +效果:;; `x.swap(y)` +抛出:;; 除非由 `key_equal` 或 `hasher` 的复制构造函数或复制赋值运算符抛出异常,否则不会抛出异常。 +备注:;; 异常规范与 C++11 标准不完全相同,因为相等谓词和哈希函数是通过它们的复制构造函数进行交换的。 + +--- + +=== erase_if +```c++ template typename unordered_map::size_type erase_if(unordered_map& c, Predicate pred); ``` + +遍历容器 `c` 并移除所有使给定谓词返回 `true` 的元素。 + +[horizontal] +返回:;; 被移除的元素数量。备注:;; 等价于:```c++auto original_size = c.size();for (auto i = c.begin(), last = c.end(); i != last; ) { if (pred(*i)) { i = c.erase(i); } else { ++i; }}return original_size - c.size();```注意,传递给 `pred` 的引用是非常量的。 + +=== 序列化 + +`unordered_map` 可以通过本库提供的 API,使用 link:../../../../../serialization/index.html[Boost.Serialization^] 进行归档/恢复。支持常规归档和 XML 归档。 + +==== 将 unordered_map 保存到归档 + +将 `unordered_map` `x` 的所有元素保存到归档(XML 归档)`ar` 中。 + +[horizontal] +要求:;; `std::remove_const::type` 和 `std::remove_const::type` 必须是可序列化的(支持 XML 序列化),并且它们支持 Boost.Serialization 的 `save_construct_data`/`load_construct_data` 协议(该协议由 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^] 类型的自动支持)。 + +--- + +==== 从归档中加载 unordered_map + +删除 `unordered_map` `x` 中所有已存在的元素,并从归档(XML 归档)`ar` 中插入由 `ar` 所读取存储中保存的原始 `unordered_map` `other` 元素的恢复副本。 + +[horizontal] +要求:;; `value_type` 可以从 `(std::remove_const::type&&, std::remove_const::type&&)` 进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^]。`x.key_equal()` 在功能上等价于 `other.key_equal()`。 +注意:;; 如果归档是使用 Boost 1.84 之前的 Boost 版本保存的,则必须全局定义配置宏 `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0` 才能使此操作成功;否则将抛出异常。 + +--- + +==== 将迭代器/常量迭代器保存到归档中 + +将 `iterator`(`const_iterator`)`it` 的位置信息保存到归档(XML 归档)`ar` 中。`it` 可以是 `end()` 迭代器。 + +[horizontal] +要求:;; `it` 所指向的 `unordered_map` `x` 此前已保存至 `ar`,并且在保存 `x` 和保存 `it` 之间未对 `x` 执行任何修改操作。 + +--- + +==== 从归档中加载迭代器/常量迭代器 + +使 `iterator`(`const_iterator`)`it` 指向保存到由归档(XML 归档)`ar` 读取的存储中的原始 `iterator`(`const_iterator`)的被恢复位置。 + +[horizontal] +要求:;; 如果 `x` 是 `it` 所指向的 `unordered_map`,则在加载 `x` 和加载 `it` 之间未对 `x` 执行任何修改操作。 diff --git a/doc/modules/ROOT/pages/reference/unordered_multimap_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/unordered_multimap_zh_Hans.adoc new file mode 100644 index 0000000..b3ecfc5 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/unordered_multimap_zh_Hans.adoc @@ -0,0 +1,1285 @@ +[#unordered_multimap] +== 类模板 unordered_multimap + +:idprefix: unordered_multimap_ + +`boost::unordered_multimap` — 一个无序关联容器,将键与对应值相关联。同一个键可以多次存储。 + +=== 概要 + +[listing,subs="+macros,+quotes"] +----- +// #include xref:reference/header_unordered_map.adoc[] + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + class unordered_multimap { + public: + // types + using key_type = Key; + using mapped_type = T; + using value_type = std::pair; + using hasher = Hash; + using key_equal = Pred; + using allocator_type = Allocator; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + using iterator = _implementation-defined_; + using const_iterator = _implementation-defined_; + using local_iterator = _implementation-defined_; + using const_local_iterator = _implementation-defined_; + using node_type = _implementation-defined_; + + // construct/copy/destroy + xref:#unordered_multimap_default_constructor[unordered_multimap](); + explicit xref:#unordered_multimap_bucket_count_constructor[unordered_multimap](size_type n, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + template + xref:#unordered_multimap_iterator_range_constructor[unordered_multimap](InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#unordered_multimap_copy_constructor[unordered_multimap](const unordered_multimap& other); + xref:#unordered_multimap_move_constructor[unordered_multimap](unordered_multimap&& other); + template + xref:#unordered_multimap_iterator_range_constructor_with_allocator[unordered_multimap](InputIterator f, InputIterator l, const allocator_type& a); + explicit xref:#unordered_multimap_allocator_constructor[unordered_multimap](const Allocator& a); + xref:#unordered_multimap_copy_constructor_with_allocator[unordered_multimap](const unordered_multimap& other, const Allocator& a); + xref:#unordered_multimap_move_constructor_with_allocator[unordered_multimap](unordered_multimap&& other, const Allocator& a); + xref:#unordered_multimap_initializer_list_constructor[unordered_multimap](std::initializer_list il, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#unordered_multimap_bucket_count_constructor_with_allocator[unordered_multimap](size_type n, const allocator_type& a); + xref:#unordered_multimap_bucket_count_constructor_with_hasher_and_allocator[unordered_multimap](size_type n, const hasher& hf, const allocator_type& a); + template + xref:#unordered_multimap_iterator_range_constructor_with_bucket_count_and_allocator[unordered_multimap](InputIterator f, InputIterator l, size_type n, const allocator_type& a); + template + xref:#unordered_multimap_iterator_range_constructor_with_bucket_count_and_hasher[unordered_multimap](InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); + xref:#unordered_multimap_initializer_list_constructor_with_allocator[unordered_multimap](std::initializer_list il, const allocator_type& a); + xref:#unordered_multimap_initializer_list_constructor_with_bucket_count_and_allocator[unordered_multimap](std::initializer_list il, size_type n, + const allocator_type& a); + xref:#unordered_multimap_initializer_list_constructor_with_bucket_count_and_hasher_and_allocator[unordered_multimap](std::initializer_list il, size_type n, const hasher& hf, + const allocator_type& a); + xref:#unordered_multimap_destructor[~unordered_multimap](); + unordered_multimap& xref:#unordered_multimap_copy_assignment[operator++=++](const unordered_multimap& other); + unordered_multimap& xref:#unordered_multimap_move_assignment[operator++=++](unordered_multimap&& other) + noexcept(boost::allocator_traits::is_always_equal::value && + boost::is_nothrow_move_assignable_v && + boost::is_nothrow_move_assignable_v); + unordered_multimap& xref:#unordered_multimap_initializer_list_assignment[operator++=++](std::initializer_list il); + allocator_type xref:#unordered_multimap_get_allocator[get_allocator]() const noexcept; + + // iterators + iterator xref:#unordered_multimap_begin[begin]() noexcept; + const_iterator xref:#unordered_multimap_begin[begin]() const noexcept; + iterator xref:#unordered_multimap_end[end]() noexcept; + const_iterator xref:#unordered_multimap_end[end]() const noexcept; + const_iterator xref:#unordered_multimap_cbegin[cbegin]() const noexcept; + const_iterator xref:#unordered_multimap_cend[cend]() const noexcept; + + // capacity + ++[[nodiscard]]++ bool xref:#unordered_multimap_empty[empty]() const noexcept; + size_type xref:#unordered_multimap_size[size]() const noexcept; + size_type xref:#unordered_multimap_max_size[max_size]() const noexcept; + + // modifiers + template iterator xref:#unordered_multimap_emplace[emplace](Args&&... args); + template iterator xref:#unordered_multimap_emplace_hint[emplace_hint](const_iterator position, Args&&... args); + iterator xref:#unordered_multimap_copy_insert[insert](const value_type& obj); + iterator xref:#unordered_multimap_move_insert[insert](value_type&& obj); + template iterator xref:#unordered_multimap_emplace_insert[insert](P&& obj); + iterator xref:#unordered_multimap_copy_insert_with_hint[insert](const_iterator hint, const value_type& obj); + iterator xref:#unordered_multimap_move_insert_with_hint[insert](const_iterator hint, value_type&& obj); + template iterator xref:#unordered_multimap_emplace_insert_with_hint[insert](const_iterator hint, P&& obj); + template void xref:#unordered_multimap_insert_iterator_range[insert](InputIterator first, InputIterator last); + void xref:#unordered_multimap_insert_initializer_list[insert](std::initializer_list il); + + node_type xref:#unordered_multimap_extract_by_iterator[extract](const_iterator position); + node_type xref:#unordered_multimap_extract_by_key[extract](const key_type& k); + template node_type xref:#unordered_multimap_extract_by_key[extract](K&& k); + iterator xref:#unordered_multimap_insert_with_node_handle[insert](node_type&& nh); + iterator xref:#unordered_multimap_insert_with_hint_and_node_handle[insert](const_iterator hint, node_type&& nh); + + iterator xref:#unordered_multimap_erase_by_position[erase](iterator position); + iterator xref:#unordered_multimap_erase_by_position[erase](const_iterator position); + size_type xref:#unordered_multimap_erase_by_key[erase](const key_type& k); + template size_type xref:#unordered_multimap_erase_by_key[erase](K&& k); + iterator xref:#unordered_multimap_erase_range[erase](const_iterator first, const_iterator last); + void xref:#unordered_multimap_quick_erase[quick_erase](const_iterator position); + void xref:#unordered_multimap_erase_return_void[erase_return_void](const_iterator position); + void xref:#unordered_multimap_swap[swap](unordered_multimap& other) + noexcept(boost::allocator_traits::is_always_equal::value && + boost::is_nothrow_swappable_v && + boost::is_nothrow_swappable_v); + void xref:#unordered_multimap_clear[clear]() noexcept; + + template + void xref:#unordered_multimap_merge[merge](unordered_multimap& source); + template + void xref:#unordered_multimap_merge[merge](unordered_multimap&& source); + template + void xref:#unordered_multimap_merge[merge](unordered_map& source); + template + void xref:#unordered_multimap_merge[merge](unordered_map&& source); + + // observers + hasher xref:#unordered_multimap_hash_function[hash_function]() const; + key_equal xref:#unordered_multimap_key_eq[key_eq]() const; + + // map operations + iterator xref:#unordered_multimap_find[find](const key_type& k); + const_iterator xref:#unordered_multimap_find[find](const key_type& k) const; + template + iterator xref:#unordered_multimap_find[find](const K& k); + template + const_iterator xref:#unordered_multimap_find[find](const K& k) const; + template + iterator xref:#unordered_multimap_find[find](CompatibleKey const& k, CompatibleHash const& hash, + CompatiblePredicate const& eq); + template + const_iterator xref:#unordered_multimap_find[find](CompatibleKey const& k, CompatibleHash const& hash, + CompatiblePredicate const& eq) const; + size_type xref:#unordered_multimap_count[count](const key_type& k) const; + template + size_type xref:#unordered_multimap_count[count](const K& k) const; + bool xref:#unordered_multimap_contains[contains](const key_type& k) const; + template + bool xref:#unordered_multimap_contains[contains](const K& k) const; + std::pair xref:#unordered_multimap_equal_range[equal_range](const key_type& k); + std::pair xref:#unordered_multimap_equal_range[equal_range](const key_type& k) const; + template + std::pair xref:#unordered_multimap_equal_range[equal_range](const K& k); + template + std::pair xref:#unordered_multimap_equal_range[equal_range](const K& k) const; + + // bucket interface + size_type xref:#unordered_multimap_bucket_count[bucket_count]() const noexcept; + size_type xref:#unordered_multimap_max_bucket_count[max_bucket_count]() const noexcept; + size_type xref:#unordered_multimap_bucket_size[bucket_size](size_type n) const; + size_type xref:#unordered_multimap_bucket[bucket](const key_type& k) const; + template size_type xref:#unordered_multimap_bucket[bucket](const K& k) const; + local_iterator xref:#unordered_multimap_begin_2[begin](size_type n); + const_local_iterator xref:#unordered_multimap_begin_2[begin](size_type n) const; + local_iterator xref:#unordered_multimap_end_2[end](size_type n); + const_local_iterator xref:#unordered_multimap_end_2[end](size_type n) const; + const_local_iterator xref:#unordered_multimap_cbegin_2[cbegin](size_type n) const; + const_local_iterator xref:#unordered_multimap_cend_2[cend](size_type n) const; + + // hash policy + float xref:#unordered_multimap_load_factor[load_factor]() const noexcept; + float xref:#unordered_multimap_max_load_factor[max_load_factor]() const noexcept; + void xref:#unordered_multimap_max_load_factor[max_load_factor](float z); + void xref:#unordered_multimap_rehash[rehash](size_type n); + void xref:#unordered_multimap_reserve[reserve](size_type n); + }; + + // Deduction Guides + template>, + class Pred = std::equal_to>, + class Allocator = std::allocator>> + unordered_multimap(InputIterator, InputIterator, typename xref:#unordered_multimap_deduction_guides[__see below__]::size_type = xref:#unordered_multimap_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_multimap, xref:#unordered_multimap_iter_mapped_type[__iter-mapped-type__], Hash, + Pred, Allocator>; + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + unordered_multimap(std::initializer_list>, + typename xref:#unordered_multimap_deduction_guides[__see below__]::size_type = xref:#unordered_multimap_deduction_guides[__see below__], Hash = Hash(), + Pred = Pred(), Allocator = Allocator()) + -> unordered_multimap; + + template + unordered_multimap(InputIterator, InputIterator, typename xref:#unordered_multimap_deduction_guides[__see below__]::size_type, Allocator) + -> unordered_multimap, xref:#unordered_multimap_iter_mapped_type[__iter-mapped-type__], + boost::hash>, + std::equal_to>, Allocator>; + + template + unordered_multimap(InputIterator, InputIterator, Allocator) + -> unordered_multimap, xref:#unordered_multimap_iter_mapped_type[__iter-mapped-type__], + boost::hash>, + std::equal_to>, Allocator>; + + template + unordered_multimap(InputIterator, InputIterator, typename xref:#unordered_multimap_deduction_guides[__see below__]::size_type, Hash, + Allocator) + -> unordered_multimap, xref:#unordered_multimap_iter_mapped_type[__iter-mapped-type__], Hash, + std::equal_to>, Allocator>; + + template + unordered_multimap(std::initializer_list>, typename xref:#unordered_multimap_deduction_guides[__see below__]::size_type, + Allocator) + -> unordered_multimap, std::equal_to, Allocator>; + + template + unordered_multimap(std::initializer_list>, Allocator) + -> unordered_multimap, std::equal_to, Allocator>; + + template + unordered_multimap(std::initializer_list>, typename xref:#unordered_multimap_deduction_guides[__see below__]::size_type, + Hash, Allocator) + -> unordered_multimap, Allocator>; + +} // namespace unordered +} // namespace boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_Key_ +|`Key` must be https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the container (i.e. `allocator_traits` can destroy it). + +|_T_ +|`T` must be https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the container (i.e. `allocator_traits` can destroy it). + +|_Hash_ +|A unary function object type that acts a hash function for a `Key`. It takes a single argument of type `Key` and returns a value of type `std::size_t`. + +|_Pred_ +|A binary function object that implements an equivalence relation on values of type `Key`. A binary function object that induces an equivalence relation on values of type `Key`. It takes two arguments of type `Key` and returns a value of type bool. + +|_Allocator_ +|An allocator whose value type is the same as the container's value type. +支持使用 https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[异形指针] 的分配器。 + +|=== + +元素被组织到桶中。具有相同哈希码的键存储在同一桶中。 + +桶的数量可以通过调用 insert 自动增加,或者作为调用 rehash 的结果。 + +=== 配置宏 + +==== `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0` + +全局定义此宏以支持加载由 Boost 1.84 之前版本的 Boost 保存到 Boost.Serialization 归档中的 `unordered_multimap`。 + +=== 类型定义 + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ iterator; +---- + +一种迭代器,其值类型为 `value_type` 。 + +迭代器类别至少为前向迭代器。 + +可转换为 `const_iterator` 。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ const_iterator; +---- + +一个常量迭代器,其值类型为 `value_type` 。 + +迭代器类别至少为前向迭代器。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ local_iterator; +---- + +一种迭代器,其值类型、差值类型以及指针和引用类型均与 iterator 相同。 + +`local_iterator` 对象可用于遍历单个桶内的元素。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ const_local_iterator; +---- + +一个常量迭代器,其值类型、差类型、指针类型和引用类型与 `const_iterator` 相同。 + +`const_local_iterator` 对象可用于遍历单个桶。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ node_type; +---- + +详见 `node_handle_map`。 + +--- + +=== 构造函数 + +==== 默认构造函数 +```c++ unordered_multimap(); ``` + +使用 `hasher()` 作为哈希函数,`key_equal()` 作为键相等谓词,`allocator_type()` 作为分配器,以及最大负载因子 `1.0` 构造一个空容器。 + +[horizontal] +后置条件:`size() == 0` +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 桶数构造函数 +```c++ explicit unordered_multimap(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,以及最大负载因子 `1.0`。 + +[horizontal] +后置条件:`size() == 0` +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template +unordered_multimap(InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,以及最大负载因子 `1.0`,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 复制构造函数 +```c++ unordered_multimap(const unordered_multimap& other); ``` + +拷贝构造函数。拷贝所包含的元素、哈希函数、谓词、最大负载因子和分配器。 + +如果 `Allocator::select_on_container_copy_construction` 存在且具有正确的签名,则分配器将根据其结果进行构造。 + +[horizontal] +要求:`value_type` 可拷贝构造。 + +--- + +==== 移动构造函数 +```c++ unordered_multimap(unordered_multimap&& other); ``` + +移动构造函数。 + +[horizontal] +注意:此函数使用 Boost.Move 实现。 +要求:`value_type` 可移动构造。 + +--- + +==== 带分配器的迭代器范围构造函数 +```c++ template unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a); ``` + +使用 `a` 作为分配器,以默认哈希函数、默认键相等谓词和最大负载因子 `1.0` 构造一个空容器,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:`hasher`、`key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 分配器构造函数 +```c++ explicit unordered_multimap(const Allocator& a); ``` + +使用分配器 `a` 构造一个空容器。 + +--- + +==== 带分配器的复制构造函数 +```c++ unordered_multimap(const unordered_multimap& other, const Allocator& a); ``` + +构造一个容器,拷贝 `other` 所包含的元素、哈希函数、谓词和最大负载因子,但使用分配器 `a`。 + +--- + +==== 带分配器的移动构造函数 +```c++ unordered_multimap(unordered_multimap&& other, const Allocator& a); ``` + +构造一个容器,移动 `other` 所包含的元素,并使用其哈希函数、谓词和最大负载因子,但使用分配器 `a`。 + +[horizontal] +注意:此函数使用 Boost.Move 实现。 +要求:`value_type` 可移动插入。 + +--- + +==== 初始化列表构造函数 +[source,c++,subs="+quotes"] +---- +unordered_multimap(std::initializer_list il, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `eql` 作为键相等性谓词、及 `a` 作为分配器,并设置最大负载因子为 `1.0` ,随后将 `il` 中的元素插入该容器。 + +[horizontal] +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- +==== 带分配器的桶数构造函数 +```c++ unordered_multimap(size_type n, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,使用默认的哈希函数和键相等谓词,`a` 作为分配器,以及最大负载因子 `1.0`。 + +[horizontal] +后置条件:`size() == 0` +要求:`hasher` 和 `key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带哈希函数和分配器的桶数构造函数 +```c++ unordered_multimap(size_type n, const hasher& hf, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,使用默认的键相等谓词,`a` 作为分配器,以及最大负载因子 `1.0`。 + +[horizontal] +后置条件:`size() == 0` +要求:`key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数和分配器的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 作为分配器,使用默认的哈希函数、默认的键相等谓词以及最大负载因子 `1.0`,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:`hasher`、`key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数和哈希函数的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、默认的键相等性谓词、 `a` 作为分配器,并设置最大负载因子为 `1.0` ,随后将区间 `[f, l)` 中的元素插入该容器。 + +[horizontal] +要求;; `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带分配器的初始化列表构造函数 + +```c++ unordered_multimap(std::initializer_list il, const allocator_type& a); ``` + +使用 `a` 作为分配器,最大负载因子为 `1.0`,构造一个空容器,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数和分配器的初始化列表构造函数 + +```c++ unordered_multimap(std::initializer_list il, size_type n, const allocator_type& a); ``` + +使用 `a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数、哈希函数和分配器的初始化列表构造函数 + +```c++ unordered_multimap(std::initializer_list il, size_type n, const hasher& hf, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`a` 作为分配器,最大负载因子为 `1.0`,并将 `il` 中的元素插入其中。 + +[horizontal] +要求;; `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +=== 析构函数 + +```c++ ~unordered_multimap(); ``` + +[horizontal] +注意:析构函数会应用于每个元素,并且所有内存都会被释放。 + +--- + +=== 赋值操作 + +==== 拷贝赋值 + +```c++ unordered_multimap& operator=(const unordered_multimap& other); ``` + +赋值运算符。拷贝所包含的元素、哈希函数、谓词和最大负载因子,但不拷贝分配器。 + +如果 `Alloc::propagate_on_container_copy_assignment` 存在且 `Alloc::propagate_on_container_copy_assignment::value` 为 `true`,则分配器会被覆盖;否则,拷贝的元素将使用现有的分配器创建。 + +[horizontal] +要求:`value_type` 可拷贝构造。 + +--- + +==== 移动赋值 +```c++ unordered_multimap& operator=(unordered_multimap&& other) noexcept(boost::allocator_traits::is_always_equal::value && boost::is_nothrow_move_assignable_v && boost::is_nothrow_move_assignable_v); ``` The move assignment operator. + +如果 `Alloc::propagate_on_container_move_assignment` 存在且 `Alloc::propagate_on_container_move_assignment::value` 为 `true`,则分配器会被覆盖;否则,移动的元素将使用现有的分配器创建。 + +[horizontal] +要求:`value_type` 可移动构造。 + +--- + +==== 初始化列表赋值 +```c++ unordered_multimap& operator=(std::initializer_list il); ``` + +从初始化列表中的值进行赋值。所有现有元素要么被新元素覆盖,要么被销毁。 + +[horizontal] +要求:`value_type` 必须能够从容器中 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^] 并且满足 https://en.cppreference.com/w/cpp/named_req/CopyAssignable[可拷贝赋值^]。 + +=== 迭代器 + +==== 开始 +```c++ iterator begin() noexcept; const_iterator begin() const noexcept; ``` + +[horizontal] +返回:指向容器第一个元素的迭代器,如果容器为空则返回容器的尾后迭代器。 + +--- + +==== 结束 +```c++ iterator end() noexcept; const_iterator end() const noexcept; ``` + +[horizontal] +返回:指向容器尾后值的迭代器。 + +--- + +==== cbegin +```c++ const_iterator cbegin() const noexcept; ``` + +[horizontal] +返回:指向容器第一个元素的 `const_iterator`,如果容器为空则返回容器的尾后值。 + +--- + +==== cend +```c++ const_iterator cend() const noexcept; ``` + +[horizontal] +返回:指向容器尾后值的 `const_iterator`。 + +--- + +=== 大小与容量 + +==== 空 + +```c++ [[nodiscard]] bool empty() const noexcept; ``` + +[horizontal] +返回:`size() == 0`。 + +--- + +==== 大小 + +```c++ size_type size() const noexcept; ``` + +[horizontal] +返回:`std::distance(begin(), end())`。 + +--- + +==== max_size + +```c++ size_type max_size() const noexcept; ``` + +[horizontal] +返回:可能的最大容器的 `size()`。 + +--- + +=== 修改器 + +==== 原地构造 +```c++ template iterator emplace(Args&&... args); ``` + +插入一个使用参数 `args` 构造的对象到容器中。 + +[horizontal] +要求:`value_type` 必须可以从 `args` 出发在 `X` 中进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^]。 +返回:指向已插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== emplace_hint +```c++ template iterator emplace_hint(const_iterator position, Args&&... args); ``` + +插入一个使用参数 `args` 构造的对象到容器中。 + +`position` 是关于元素插入位置的建议。 + +[horizontal] +要求:`value_type` 必须可以从 `args` 出发在 `X` 中进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^]。 +返回:指向已插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:标准关于 `position` 的含义表述相当模糊。但唯一实际的使用方式,也是 Boost.Unordered 支持的唯一方式,是将其指向一个具有相同键的已存在元素。 +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 拷贝插入 +```c++ iterator insert(const value_type& obj); ``` + +将 `obj` 插入容器中。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 +返回:指向已插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 移动插入 +```c++ iterator insert(value_type&& obj); ``` + +将 `obj` 插入容器中。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^]。 +返回:指向已插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 原地插入 +```c++ template iterator insert(P&& obj); ``` + +通过执行 `emplace(std::forward

(value))` 向容器中插入一个元素。

+ +仅在 `std::is_constructible::value` 为 `true` 时参与重载决议。 + +[horizontal] +返回:指向已插入元素的迭代器。 + +--- + +==== 带提示的拷贝插入 +```c++iterator insert(const_iterator hint, const value_type& obj);``` +将 `obj` 插入容器中。 + +`hint` 是关于元素插入位置的建议。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 +返回:指向已插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:标准关于 `hint` 的含义表述相当模糊。但唯一实际的使用方式,也是 Boost.Unordered 支持的唯一方式,是将其指向一个具有相同键的已存在元素。 +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 带提示的移动插入 +```c++ iterator insert(const_iterator hint, value_type&& obj); ``` + +将 `obj` 插入容器中。 + +`hint` 是关于元素插入位置的建议。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^]。 +返回:指向已插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:标准关于 `hint` 的含义表述相当模糊。但唯一实际的使用方式,也是 Boost.Unordered 支持的唯一方式,是将其指向一个具有相同键的已存在元素。 +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 带提示的原地插入 +```c++ template iterator insert(const_iterator hint, P&& obj); ``` + +通过执行 `emplace_hint(hint, std::forward

(value))` 向容器中插入一个元素。

+ +仅在 `std::is_constructible::value` 为 `true` 时参与重载决议。 + +`hint` 是关于元素插入位置的建议。 + +[horizontal] +返回:指向已插入元素的迭代器。 +注意:标准关于 `hint` 的含义表述相当模糊。但唯一实际的使用方式,也是 Boost.Unordered 支持的唯一方式,是将其指向一个具有相同键的已存在元素。 +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 迭代器范围插入 +```c++ template void insert(InputIterator first, InputIterator last); ``` + +将一个范围内的元素插入容器中。 + +[horizontal] +要求:`value_type` 必须可以从 `*first` 出发在 `X` 中进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^]。 +抛出:当插入单个元素时,如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 初始化列表插入 +```c++ void insert(std::initializer_list il); ``` + +将一个范围内的元素插入容器中。 + +[horizontal] +要求:`value_type` 可以在容器中进行 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 +抛出:当插入单个元素时,如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 通过迭代器提取 +```c++ node_type extract(const_iterator position); ``` + +移除 `position` 所指向的元素。 + +[horizontal] +返回:一个拥有该元素的 `node_type`。 +注意:通过此方法提取的节点可以插入到兼容的 `unordered_map` 中。 + +--- + +==== 通过键提取 +```c++ node_type extract(const key_type& k); template node_type extract(K&& k); ``` + +移除一个键与 `k` 等价的元素。 + +[horizontal] +返回:如果找到该元素,则返回一个拥有该元素的 `node_type`;否则返回一个空的 `node_type`。 +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +注意:通过此方法提取的节点可以插入到兼容的 `unordered_map` 中。 + +`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 都不能从 `K` 隐式转换时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 通过 `node_handle` 插入 +```c++ iterator insert(node_type&& nh); ``` + +如果 `nh` 为空,则无效果。 + +否则,插入 `nh` 所拥有的元素。 + +[horizontal] +要求:`nh` 为空,或者 `nh.get_allocator()` 等于容器的分配器。 +返回:如果 `nh` 为空,则返回 `end()`。 +否则,返回指向新插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 +此函数可用于插入从兼容的 `unordered_map` 中提取的节点。 + +--- + +==== 带提示和 `node_handle` 的插入 +```c++ iterator insert(const_iterator hint, node_type&& nh); ``` + +如果 `nh` 为空,则无效果。 + +否则,插入 `nh` 所拥有的元素。 + +`hint` 是关于元素插入位置的建议。 + +[horizontal] +要求:`nh` 为空,或者 `nh.get_allocator()` 等于容器的分配器。 +返回:如果 `nh` 为空,则返回 `end()`;否则,返回指向新插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:标准关于 `hint` 的含义表述相当模糊。但唯一实际的使用方式,也是 Boost.Unordered 支持的唯一方式,是将其指向一个具有相同键的已存在元素。 +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 +此函数可用于插入从兼容的 `unordered_map` 中提取的节点。 + +--- + +==== 通过位置擦除 + +```c++ iterator erase(iterator position); iterator erase(const_iterator position); ``` + +擦除 `position` 所指向的元素。 + +[horizontal] +返回:`position` 在被擦除之前的后一个迭代器。 +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +注意:在旧版本中,此操作可能效率较低,因为它需要搜索多个桶以找到返回迭代器的位置。数据结构已经更改,不再是这种情况,并且备用的擦除方法已被弃用。 + +--- + +==== 通过键擦除 +```c++ size_type erase(const key_type& k); template size_type erase(K&& k); ``` + +擦除所有键与 `k` 等价的元素。 + +[horizontal] +返回:被擦除的元素数量。 +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 都不能从 `K` 隐式转换时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 范围擦除 + +```c++ iterator erase(const_iterator first, const_iterator last); ``` + +擦除从 `first` 到 `last` 范围内的元素。 + +[horizontal] +返回:被擦除元素之后的迭代器——即 `last`。 +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +在此实现中,此重载不会调用任何函数对象的方法,因此不会抛出异常,但这在其他实现中可能并不成立。 + +--- + +==== quick_erase +```c++ void quick_erase(const_iterator position); ``` + +擦除 `position` 所指向的元素。 + +[horizontal] +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +在此实现中,此重载不会调用任何函数对象的方法,因此不会抛出异常,但这在其他实现中可能并不成立。 +注意:引入此方法是因为从 `erase` 返回指向下一个元素的迭代器成本较高,但容器已经过重新设计,不再存在这种情况。因此,此方法现已弃用。 + +--- + +==== erase_return_void +```c++ void erase_return_void(const_iterator position); ``` + +擦除 `position` 所指向的元素。 + +[horizontal] +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +在此实现中,此重载不会调用任何函数对象的方法,因此不会抛出异常,但这在其他实现中可能并不成立。 +注意:引入此方法是因为从 `erase` 返回指向下一个元素的迭代器成本较高,但容器已经过重新设计,不再存在这种情况。因此,此方法现已弃用。 + +--- + +==== 交换 +```c++ void swap(unordered_multimap& other) noexcept(boost::allocator_traits::is_always_equal::value && boost::is_nothrow_swappable_v && boost::is_nothrow_swappable_v); ``` + +交换容器的内容与参数的内容。 + +如果 `Allocator::propagate_on_container_swap` 被声明且 `Allocator::propagate_on_container_swap::value` 为 `true`,则交换容器的分配器。否则,使用不相等的分配器进行交换将导致未定义行为。 + +[horizontal] +抛出:除非 `key_equal` 或 `hasher` 的拷贝构造函数或拷贝赋值运算符抛出异常,否则不抛出任何异常。 +注意:异常规范与 C++11 标准不完全相同,因为相等谓词和哈希函数是使用其拷贝构造函数进行交换的。 + +--- + +==== 清空 +```c++ void clear() noexcept; ``` + +擦除容器中的所有元素。 + +[horizontal] +后置条件:`size() == 0` +抛出:永不抛出异常。 + +--- + +==== 合并 +```c++ template void merge(unordered_multimap& source); template void merge(unordered_multimap&& source); template void merge(unordered_map& source); template void merge(unordered_map&& source); ``` + +尝试“合并”两个容器:遍历 `source`,提取 `source` 中的所有节点,并将它们插入到 `*this` 中。 + +由于 `source` 可能具有不同的哈希函数和键相等谓词,因此 `source` 中每个节点的键都会使用 `this->hash_function()` 重新计算哈希值,然后在必要时使用 `this->key_eq()` 进行比较。 + +如果 `this->get_allocator() != source.get_allocator()`,则此函数的行为未定义。 + +此函数不会拷贝或移动任何元素,而是仅仅将节点从 `source` 重新定位到 `*this` 中。 + +[horizontal] +注意:;; + -- +* 指向被转移元素的指针和引用保持有效。 +* 使被转移元素的迭代器失效。 +* 使属于 `*this` 的迭代器失效。 +* 指向 `source` 中未被转移元素的迭代器保持有效。 +-- + +--- + +=== 观察器 + +==== get_allocator +``` allocator_type get_allocator() const; ``` + +--- + +==== 哈希函数 +``` hasher hash_function() const; ``` + +[horizontal] +返回:容器的哈希函数。 + +--- + +==== key_eq +``` key_equal key_eq() const; ``` + +[horizontal] +返回:容器的键相等谓词。 + +--- + +=== 查找 + +==== find +```c++ iterator find(const key_type& k); const_iterator find(const key_type& k) const; template iterator find(const K& k); template const_iterator find(const K& k) const; template iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq); template const_iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const; + +``` + +[horizontal] +返回:指向键与 `k` 等价的元素的迭代器;如果不存在这样的元素,则返回 `b.end()`。 +注意:包含 `CompatibleKey`、`CompatibleHash` 和 `CompatiblePredicate` 的模板化重载是非标准扩展,允许您为不同类型的键使用兼容的哈希函数和相等谓词,以避免昂贵的类型转换。通常不鼓励使用它们,而应使用 `K` 成员函数模板。 `template` 重载仅在 `Hash::is_transparent` `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== count +```c++ size_type count(const key_type& k) const; template size_type count(const K& k) const; ``` + +[horizontal] +返回:键与 `k` 等价的元素数量。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 包含 +```c++ bool contains(const key_type& k) const; template bool contains(const K& k) const; ``` + +[horizontal] +返回:一个布尔值,指示容器中是否存在键等于 `key` 的元素。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== equal_range +```c++ std::pair equal_range(const key_type& k); std::pair equal_range(const key_type& k) const; template std::pair equal_range(const K& k); template std::pair equal_range(const K& k) const; ``` + +[horizontal] +返回:包含所有键与 `k` 等价的元素的范围。如果容器不包含任何此类元素,则返回 `std::make_pair(b.end(), b.end())`。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +=== 桶接口 + +==== bucket_count +```c++ size_type bucket_count() const noexcept; ``` + +[horizontal] +返回:桶的数量。 + +--- + +==== max_bucket_count +```c++ size_type max_bucket_count() const noexcept; ``` + +[horizontal] +返回:桶数量的上界。 + +--- + +==== 桶大小 +```c++ size_type bucket_size(size_type n) const; ``` + +[horizontal] +要求:`n < bucket_count()` +返回:桶 `n` 中的元素数量。 + +--- + +==== 桶 +```c++ size_type bucket(const key_type& k) const; template size_type bucket(const K& k) const; ``` + +[horizontal] +返回:将包含键为 `k` 的元素的桶的索引。 +后置条件:返回值小于 `bucket_count()`。 + +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 开始 + +```c++ local_iterator begin(size_type n); const_local_iterator begin(size_type n) const; ``` + +[horizontal] +要求:`n` 必须在 `[0, bucket_count())` 范围内。 +返回:指向索引为 `n` 的桶中第一个元素的局部迭代器。 + +--- + +==== 结束 +```c++ local_iterator end(size_type n); const_local_iterator end(size_type n) const; ``` + +[horizontal] +要求:`n` 必须在 `[0, bucket_count())` 范围内。 +返回:指向索引为 `n` 的桶中“尾后”元素的局部迭代器。 + +--- + +==== cbegin +```c++ const_local_iterator cbegin(size_type n) const; ``` + +[horizontal] +要求:`n` 必须在 `[0, bucket_count())` 范围内。 +返回:指向索引为 `n` 的桶中第一个元素的常量局部迭代器。 + +--- + +==== cend +```c++ const_local_iterator cend(size_type n) const; ``` + +[horizontal] +要求:`n` 必须在 `[0, bucket_count())` 范围内。 +返回:指向索引为 `n` 的桶中“尾后”元素的常量局部迭代器。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ float load_factor() const noexcept; ``` + +[horizontal] +返回:每个桶的平均元素数量。 + +--- + +==== max_load_factor + +```c++ float max_load_factor() const noexcept; ``` + +[horizontal] +返回:当前最大负载因子。 + +--- + +==== 设置最大负载因子 +```c++ void max_load_factor(float z); ``` + +[horizontal] +效果:使用 `z` 作为提示,更改容器的最大负载因子。 + +--- + + +==== 重哈希 +```c++ void rehash(size_type n); ``` + +更改桶的数量,使其至少包含 `n` 个桶,并且使得负载因子小于或等于最大负载因子。在适用的情况下,这将增大或缩小与容器关联的 `bucket_count()`。 + +当 `size() == 0` 时,`rehash(0)` 将释放底层的桶数组。 + +使迭代器失效,并改变元素的顺序。指向元素的指针和引用不会失效。 + +[horizontal] +抛出:如果抛出异常(除非是由容器的哈希函数或比较函数抛出的),则该函数无效果。 + +--- + +==== 保留 +```c++ void reserve(size_type n); ``` + +等价于 `a.rehash(ceil(n / a.max_load_factor()))`;如果 `n > 0` 且 `a.max_load_factor() == std::numeric_limits::infinity()`,则等价于 `a.rehash(1)`。 + +与 `rehash` 类似,此函数可用于增大或缩小容器中的桶数量。 + +使迭代器失效,并改变元素的顺序。指向元素的指针和引用不会失效。 + +[horizontal] +抛出:如果抛出异常(除非是由容器的哈希函数或比较函数抛出的),则该函数无效果。 + +--- + +=== 推导指引 +如果满足以下任一条件,则推导指引不会参与重载决议: + +- 它具有一个 `InputIterator` 模板参数,并且为该参数推导出的类型不符合输入迭代器的要求。 +- 它具有一个 `Allocator` 模板参数,并且为该参数推导出的类型不符合分配器的要求。 +- 它具有一个 `Hash` 模板参数,并且为该参数推导出的类型是整数类型或符合分配器的要求。 +- 它具有一个 `Pred` 模板参数,并且为该参数推导出的类型符合分配器的要求。 + +推导指引中的 `size_type` 参数类型指的是由该推导指引所推导出的容器类型的 `size_type` 成员类型。其默认值与所选构造函数的默认值一致。 + +==== _iter-value-type_ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-value-type__ = + typename std::iterator_traits::value_type; // exposition only +----- + +==== __iter-key-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-key-type__ = std::remove_const_t< + std::tuple_element_t<0, xref:#unordered_multimap_iter_value_type[__iter-value-type__]>>; // exposition only +----- + +==== __iter-mapped-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-mapped-type__ = + std::tuple_element_t<1, xref:#unordered_multimap_iter_value_type[__iter-value-type__]>; // exposition only +----- + +==== __iter-to-alloc-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-to-alloc-type__ = std::pair< + std::add_const_t>>, + std::tuple_element_t<1, xref:#unordered_multimap_iter_value_type[__iter-value-type__]>>; // exposition only +----- + +=== 相等性比较 + +==== 运算符 +```c++ template bool operator==(const unordered_multimap& x, const unordered_multimap& y); ``` + +若 `x.size() == y.size()` 且对于 `x` 中的每个等价键组,在 `y` 中均存在一个具有相同键的组,并且该组是 `x` 中对应组的置换(使用 `operator==` 比较值类型),则返回 `true`。 + +[horizontal] +注意:如果两个容器不具有等价的相等谓词,则行为未定义。 + +--- + +==== operator! +```c++ template bool operator!=(const unordered_multimap& x, const unordered_multimap& y); ``` + +若 `x.size() == y.size()` 且对于 `x` 中的每个等价键组,在 `y` 中均存在一个具有相同键的组,并且该组是 `x` 中对应组的置换(使用 `operator==` 比较值类型),则返回 `false`。 + +[horizontal] +注意:如果两个容器不具有等价的相等谓词,则行为未定义。 + +--- + +=== 交换 +```c++ template void swap(unordered_multimap& x, unordered_multimap& y) noexcept(noexcept(x.swap(y))); ``` + +交换 x 和 y 的内容。 + +如果 `Allocator::propagate_on_container_swap` 被声明且 `Allocator::propagate_on_container_swap::value` 为 `true`,则交换容器的分配器。否则,使用不相等的分配器进行交换将导致未定义行为。 + +[horizontal] +效果:`x.swap(y)` +抛出:除非 `key_equal` 或 `hasher` 的拷贝构造函数或拷贝赋值运算符抛出异常,否则不抛出任何异常。 +注意:异常规范与 C++11 标准不完全相同,因为相等谓词和哈希函数是使用其拷贝构造函数进行交换的。 + +--- + +=== erase_if +```c++ template typename unordered_multimap::size_type erase_if(unordered_multimap& c, Predicate pred); ``` + +遍历容器 `c`,并移除所有使给定谓词返回 `true` 的元素。 + +[horizontal] +返回:被擦除的元素数量。 注意:等价于: +```c++auto original_size = c.size();for (auto i = c.begin(), last = c.end(); i != last; ) {if (pred(*i)) {i = c.erase(i);} else {++i;}}return original_size - c.size(); +``` 注意,传递给 `pred` 的引用是非常量的。 + +=== 序列化 + +`unordered_multimap` 可以通过本库提供的 API,借助 link:../../../../../serialization/index.html[Boost.Serialization^] 进行归档/恢复。支持常规归档和 XML 归档。 + +==== 将 unordered_multimap 保存到归档 + +将 `unordered_multimap` 容器 `x` 的所有元素保存到归档(XML 归档) `ar` 中。 + +[horizontal] +要求:`std::remove_const::type` 和 `std::remove_const::type` 必须是可序列化的(对于 XML 归档需支持 XML 序列化),并且它们必须支持 Boost.Serialization 的 `save_construct_data`/`load_construct_data` 协议(该协议由满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 的类型自动支持)。 + +--- + +==== 从归档加载 unordered_multimap + +删除 `unordered_multimap` 容器 `x` 中所有已存在的元素,并从归档(XML 归档)`ar` 中插入从原始 `unordered_multimap` 容器 `other` 保存到 `ar` 所读取存储中的元素恢复出的副本。 + +[horizontal] +要求:`value_type` 必须可以从 `(std::remove_const::type&&, std::remove_const::type&&)` 进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^]。`x.key_equal()` 在功能上必须等价于 `other.key_equal()`。 +注意:如果归档是使用 Boost 1.84 之前版本的 Boost 保存的,则必须全局定义配置宏 `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0` 才能使此操作成功;否则,将抛出异常。 + +--- + +==== 将迭代器/常量迭代器保存到归档 + +将迭代器(或常量迭代器)`it` 的位置信息保存到归档(XML 归档)`ar` 中。`it` 可以是一个 `end()` 迭代器。 + +[horizontal] +要求:`it` 所指向的 `unordered_multimap` 容器 `x` 必须先被保存到 `ar` 中,并且在保存 `x` 和保存 `it` 之间,不能对 `x` 执行任何修改操作。 + +--- + +==== 从归档加载迭代器/常量迭代器 + +使迭代器(或常量迭代器)`it` 指向原始迭代器(或常量迭代器)被保存到归档(XML 归档)`ar` 所读取存储中的位置恢复后的位置。 + +[horizontal] +要求:如果 `x` 是 `it` 所指向的 `unordered_multimap` 容器,则在加载 `x` 和加载 `it` 之间,不能对 `x` 执行任何修改操作。 diff --git a/doc/modules/ROOT/pages/reference/unordered_multiset_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/unordered_multiset_zh_Hans.adoc new file mode 100644 index 0000000..4fb88c5 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/unordered_multiset_zh_Hans.adoc @@ -0,0 +1,1218 @@ +[#unordered_multiset] +== 类模板 unordered_multiset + +:idprefix: unordered_multiset_ + +`boost::unordered_multiset` — 一个存储值的无序关联容器。同一个键可以多次存储。 + +=== 概要 + +[listing,subs="+macros,+quotes"] +----- +// #include xref:reference/header_unordered_set.adoc[] + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator> + class unordered_multiset { + public: + // types + using key_type = Key; + using value_type = Key; + using hasher = Hash; + using key_equal = Pred; + using allocator_type = Allocator; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + using iterator = _implementation-defined_; + using const_iterator = _implementation-defined_; + using local_iterator = _implementation-defined_; + using const_local_iterator = _implementation-defined_; + using node_type = _implementation-defined_; + + // construct/copy/destroy + xref:#unordered_multiset_default_constructor[unordered_multiset](); + explicit xref:#unordered_multiset_bucket_count_constructor[unordered_multiset](size_type n, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + template + xref:#unordered_multiset_iterator_range_constructor[unordered_multiset](InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#unordered_multiset_copy_constructor[unordered_multiset](const unordered_multiset& other); + xref:#unordered_multiset_move_constructor[unordered_multiset](unordered_multiset&& other); + template + xref:#unordered_multiset_iterator_range_constructor_with_allocator[unordered_multiset](InputIterator f, InputIterator l, const allocator_type& a); + explicit xref:#unordered_multiset_allocator_constructor[unordered_multiset](const Allocator& a); + xref:#unordered_multiset_copy_constructor_with_allocator[unordered_multiset](const unordered_multiset& other, const Allocator& a); + xref:#unordered_multiset_move_constructor_with_allocator[unordered_multiset](unordered_multiset&& other, const Allocator& a); + xref:#unordered_multiset_initializer_list_constructor[unordered_multiset](std::initializer_list il, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#unordered_multiset_bucket_count_constructor_with_allocator[unordered_multiset](size_type n, const allocator_type& a); + xref:#unordered_multiset_bucket_count_constructor_with_hasher_and_allocator[unordered_multiset](size_type n, const hasher& hf, const allocator_type& a); + template + xref:#unordered_multiset_iterator_range_constructor_with_bucket_count_and_allocator[unordered_multiset](InputIterator f, InputIterator l, size_type n, const allocator_type& a); + template + xref:#unordered_multiset_iterator_range_constructor_with_bucket_count_and_hasher[unordered_multiset](InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); + xref:#unordered_multiset_initializer_list_constructor_with_allocator[unordered_multiset](std::initializer_list il, const allocator_type& a); + xref:#unordered_multiset_initializer_list_constructor_with_bucket_count_and_allocator[unordered_multiset](std::initializer_list il, size_type n, + const allocator_type& a) + xref:#unordered_multiset_initializer_list_constructor_with_bucket_count_and_hasher_and_allocator[unordered_multiset](std::initializer_list il, size_type n, const hasher& hf, + const allocator_type& a); + xref:#unordered_multiset_destructor[~unordered_multiset()]; + unordered_multiset& xref:#unordered_multiset_copy_assignment[operator++=++](const unordered_multiset& other); + unordered_multiset& xref:#unordered_multiset_move_assignment[operator++=++](unordered_multiset&& other) + noexcept(boost::allocator_traits::is_always_equal::value && + boost::is_nothrow_move_assignable_v && + boost::is_nothrow_move_assignable_v); + unordered_multiset& xref:#unordered_multiset_initializer_list_assignment[operator++=++](std::initializer_list il); + allocator_type xref:#unordered_multiset_get_allocator[get_allocator]() const noexcept; + + // iterators + iterator xref:#unordered_multiset_begin[begin]() noexcept; + const_iterator xref:#unordered_multiset_begin[begin]() const noexcept; + iterator xref:#unordered_multiset_end[end]() noexcept; + const_iterator xref:#unordered_multiset_end[end]() const noexcept; + const_iterator xref:#unordered_multiset_cbegin[cbegin]() const noexcept; + const_iterator xref:#unordered_multiset_cend[cend]() const noexcept; + + // capacity + ++[[nodiscard]]++ bool xref:#unordered_multiset_empty[empty]() const noexcept; + size_type xref:#unordered_multiset_size[size]() const noexcept; + size_type xref:#unordered_multiset_max_size[max_size]() const noexcept; + + // modifiers + template iterator xref:#unordered_multiset_emplace[emplace](Args&&... args); + template iterator xref:#unordered_multiset_emplace_hint[emplace_hint](const_iterator position, Args&&... args); + iterator xref:#unordered_multiset_copy_insert[insert](const value_type& obj); + iterator xref:#unordered_multiset_move_insert[insert](value_type&& obj); + iterator xref:#unordered_multiset_copy_insert_with_hint[insert](const_iterator hint, const value_type& obj); + iterator xref:#unordered_multiset_move_insert_with_hint[insert](const_iterator hint, value_type&& obj); + template void xref:#unordered_multiset_insert_iterator_range[insert](InputIterator first, InputIterator last); + void xref:#unordered_multiset_insert_initializer_list[insert](std::initializer_list il); + + node_type xref:#unordered_multiset_extract_by_iterator[extract](const_iterator position); + node_type xref:#unordered_multiset_extract_by_value[extract](const key_type& k); + template node_type xref:#unordered_multiset_extract_by_value[extract](K&& k); + iterator xref:#unordered_multiset_insert_with_node_handle[insert](node_type&& nh); + iterator xref:#unordered_multiset_insert_with_hint_and_node_handle[insert](const_iterator hint, node_type&& nh); + + iterator xref:#unordered_multiset_erase_by_position[erase](iterator position); + iterator xref:#unordered_multiset_erase_by_position[erase](const_iterator position); + size_type xref:#unordered_multiset_erase_by_value[erase](const key_type& k); + template size_type xref:#unordered_multiset_erase_by_value[erase](K&& x); + iterator xref:#unordered_multiset_erase_range[erase](const_iterator first, const_iterator last); + void xref:#unordered_multiset_quick_erase[quick_erase](const_iterator position); + void xref:#unordered_multiset_erase_return_void[erase_return_void](const_iterator position); + void xref:#unordered_multiset_swap[swap](unordered_multiset&) + noexcept(boost::allocator_traits::is_always_equal::value && + boost::is_nothrow_swappable_v && + boost::is_nothrow_swappable_v); + void xref:#unordered_multiset_clear[clear]() noexcept; + + template + void xref:#unordered_multiset_merge[merge](unordered_multiset& source); + template + void xref:#unordered_multiset_merge[merge](unordered_multiset&& source); + template + void xref:#unordered_multiset_merge[merge](unordered_set& source); + template + void xref:#unordered_multiset_merge[merge](unordered_set&& source); + + // observers + hasher xref:#unordered_multiset_hash_function[hash_function]() const; + key_equal xref:#unordered_multiset_key_eq[key_eq]() const; + + // set operations + iterator xref:#unordered_multiset_find[find](const key_type& k); + const_iterator xref:#unordered_multiset_find[find](const key_type& k) const; + template + iterator xref:#unordered_multiset_find[find](const K& k); + template + const_iterator xref:#unordered_multiset_find[find](const K& k) const; + template + iterator xref:#unordered_multiset_find[find](CompatibleKey const&, CompatibleHash const&, + CompatiblePredicate const&); + template + const_iterator xref:#unordered_multiset_find[find](CompatibleKey const&, CompatibleHash const&, + CompatiblePredicate const&) const; + size_type xref:#unordered_multiset_count[count](const key_type& k) const; + template + size_type xref:#unordered_multiset_count[count](const K& k) const; + bool xref:#unordered_multiset_contains[contains](const key_type& k) const; + template + bool xref:#unordered_multiset_contains[contains](const K& k) const; + std::pair xref:#unordered_multiset_equal_range[equal_range](const key_type& k); + std::pair xref:#unordered_multiset_equal_range[equal_range](const key_type& k) const; + template + std::pair xref:#unordered_multiset_equal_range[equal_range](const K& k); + template + std::pair xref:#unordered_multiset_equal_range[equal_range](const K& k) const; + + // bucket interface + size_type xref:#unordered_multiset_bucket_count[bucket_count]() const noexcept; + size_type xref:#unordered_multiset_max_bucket_count[max_bucket_count]() const noexcept; + size_type xref:#unordered_multiset_bucket_size[bucket_size](size_type n) const; + size_type xref:#unordered_multiset_bucket[bucket](const key_type& k) const; + template size_type xref:#unordered_multiset_bucket[bucket](const K& k) const; + local_iterator xref:#unordered_multiset_begin_2[begin](size_type n); + const_local_iterator xref:#unordered_multiset_begin_2[begin](size_type n) const; + local_iterator xref:#unordered_multiset_end_2[end](size_type n); + const_local_iterator xref:#unordered_multiset_end_2[end](size_type n) const; + const_local_iterator xref:#unordered_multiset_cbegin_2[cbegin](size_type n) const; + const_local_iterator xref:#unordered_multiset_cend_2[cend](size_type n) const; + + // hash policy + float xref:#unordered_multiset_load_factor[load_factor]() const noexcept; + float xref:#unordered_multiset_max_load_factor[max_load_factor]() const noexcept; + void xref:#unordered_multiset_set_max_load_factor[max_load_factor](float z); + void xref:#unordered_multiset_rehash[rehash](size_type n); + void xref:#unordered_multiset_reserve[reserve](size_type n); + }; + + // Deduction Guides + template>, + class Pred = std::equal_to>, + class Allocator = std::allocator>> + unordered_multiset(InputIterator, InputIterator, typename xref:#unordered_multiset_deduction_guides[__see below__]::size_type = xref:#unordered_multiset_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_multiset, Hash, Pred, Allocator>; + + template, class Pred = std::equal_to, + class Allocator = std::allocator> + unordered_multiset(std::initializer_list, typename xref:#unordered_multiset_deduction_guides[__see below__]::size_type = xref:#unordered_multiset_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_multiset; + + template + unordered_multiset(InputIterator, InputIterator, typename xref:#unordered_multiset_deduction_guides[__see below__]::size_type, Allocator) + -> unordered_multiset, + boost::hash>, + std::equal_to>, Allocator>; + + template + unordered_multiset(InputIterator, InputIterator, Allocator) + -> unordered_multiset, + boost::hash>, + std::equal_to>, Allocator>; + + template + unordered_multiset(InputIterator, InputIterator, typename xref:#unordered_multiset_deduction_guides[__see below__]::size_type, Hash, + Allocator) + -> unordered_multiset, Hash, + std::equal_to>, Allocator>; + + template + unordered_multiset(std::initializer_list, typename xref:#unordered_multiset_deduction_guides[__see below__]::size_type, Allocator) + -> unordered_multiset, std::equal_to, Allocator>; + + template + unordered_multiset(std::initializer_list, Allocator) + -> unordered_multiset, std::equal_to, Allocator>; + + template + unordered_multiset(std::initializer_list, typename xref:#unordered_multiset_deduction_guides[__see below__]::size_type, Hash, Allocator) + -> unordered_multiset, Allocator>; + +} // namespace unordered +} // namespace boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_Key_ +|`Key` must be https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the container (i.e. `allocator_traits` can destroy it). + +|_Hash_ +|A unary function object type that acts a hash function for a `Key`. It takes a single argument of type `Key` and returns a value of type `std::size_t`. + +|_Pred_ +|A binary function object that implements an equivalence relation on values of type `Key`. A binary function object that induces an equivalence relation on values of type `Key`. It takes two arguments of type `Key` and returns a value of type bool. + +|_Allocator_ +|An allocator whose value type is the same as the container's value type. +支持使用 https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[异形指针] 的分配器。 + +|=== + +元素被组织到桶中。具有相同哈希码的键存储在同一桶中,并且具有等价键的元素彼此相邻存储。 + +桶的数量可以通过调用 insert 自动增加,或者作为调用 rehash 的结果。 + +=== 配置宏 + +==== `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0` + +全局定义此宏以支持加载由 Boost 1.84 之前版本的 Boost 保存到 Boost.Serialization 归档中的 `unordered_multiset`。 + +=== 类型定义 + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ iterator; +---- + +一个常量迭代器,其值类型为 `value_type`。 + +迭代器类别至少为前向迭代器。 + +可转换为 `const_iterator` 。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ const_iterator; +---- + +一个常量迭代器,其值类型为 `value_type`。 + +迭代器类别至少为前向迭代器。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ local_iterator; +---- + +一种迭代器,其值类型、差值类型以及指针和引用类型均与 iterator 相同。 + +`local_iterator` 对象可用于遍历单个桶内的元素。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ const_local_iterator; +---- + +一种常量迭代器,其值类型、差值类型以及指针和引用类型均与 const_iterator 相同。 + +`const_local_iterator` 对象可用于遍历单个桶。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ node_type; +---- + +详见 `node_handle_set`。 + +--- + +=== 构造函数 + +==== 默认构造函数 +```c++ unordered_multiset(); ``` + +使用 `hasher()` 作为哈希函数,`key_equal()` 作为键相等谓词,`allocator_type()` 作为分配器,以及最大负载因子 `1.0` 构造一个空容器。 + +[horizontal] +后置条件:`size() == 0` +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 桶数构造函数 +```c++ explicit unordered_multiset(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,以及最大负载因子 `1.0`。 + +[horizontal] +后置条件:`size() == 0` +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_multiset(InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,以及最大负载因子 `1.0`,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 复制构造函数 +```c++ unordered_multiset(const unordered_multiset& other); ``` + +拷贝构造函数。拷贝所包含的元素、哈希函数、谓词、最大负载因子和分配器。 + +如果 `Allocator::select_on_container_copy_construction` 存在且具有正确的签名,则分配器将根据其结果进行构造。 + +[horizontal] +要求:`value_type` 可拷贝构造。 + +--- + +==== 移动构造函数 +```c++ unordered_multiset(unordered_multiset&& other); ``` + +移动构造函数。 + +[horizontal] +注意:此函数使用 Boost.Move 实现。 +要求:`value_type` 可移动构造。 + +--- + +==== 带分配器的迭代器范围构造函数 +```c++ template unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a); ``` + +使用 `a` 作为分配器,以默认哈希函数、默认键相等谓词和最大负载因子 `1.0` 构造一个空容器,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:`hasher`、`key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 分配器构造函数 +```c++ explicit unordered_multiset(const Allocator& a); ``` + +使用分配器 `a` 构造一个空容器。 + +--- + +==== 带分配器的拷贝构造函数 +```c++ unordered_multiset(const unordered_multiset& other, const Allocator& a); ``` + +构造一个容器,拷贝 `other` 所包含的元素、哈希函数、谓词和最大负载因子,但使用分配器 `a`。 + +--- + +==== 带分配器的移动构造函数 +```c++ unordered_multiset(unordered_multiset&& other, const Allocator& a); ``` + +构造一个容器,移动 `other` 所包含的元素,并使用其哈希函数、谓词和最大负载因子,但使用分配器 `a`。 + +[horizontal] +注意:此函数使用 Boost.Move 实现。 +要求:`value_type` 可移动插入。 + +--- + +==== 初始化列表构造函数 +[source,c++,subs="+quotes"] +---- +unordered_multiset(std::initializer_list il, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,以及最大负载因子 `1.0`,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带分配器的桶数构造函数 +```c++ unordered_multiset(size_type n, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,使用默认的哈希函数和键相等谓词,`a` 作为分配器,以及最大负载因子 `1.0`。 + +[horizontal] +后置条件:`size() == 0` +要求:`hasher` 和 `key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带哈希函数和分配器的桶数构造函数 +```c++ unordered_multiset(size_type n, const hasher& hf, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,使用默认的键相等谓词,`a` 作为分配器,以及最大负载因子 `1.0`。 + +[horizontal] +后置条件:`size() == 0` 要求:`key_equal` 需要满足https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数和分配器的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 作为分配器,使用默认的哈希函数、默认的键相等谓词以及最大负载因子 `1.0`,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:`hasher`、`key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数和哈希函数的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、默认的键相等性谓词、 `a` 作为分配器,并设置最大负载因子为 `1.0` ,随后将区间 `[f, l)` 中的元素插入该容器。 + +[horizontal] +要求;; `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带分配器的初始化列表构造函数 + +```c++ unordered_multiset(std::initializer_list il, const allocator_type& a); ``` + +使用 `a` 作为分配器,最大负载因子为 `1.0`,构造一个空容器,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数和分配器的初始化列表构造函数 + +```c++ unordered_multiset(std::initializer_list il, size_type n, const allocator_type& a) ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 作为分配器,最大负载因子为 `1.0`,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数、哈希函数和分配器的初始化列表构造函数 + +```c++ unordered_multiset(std::initializer_list il, size_type n, const hasher& hf, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`a` 作为分配器,最大负载因子为 `1.0`,并将 `il` 中的元素插入其中。 + +[horizontal] +要求;; `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +=== 析构函数 + +```c++ ~unordered_multiset(); ``` + +[horizontal] +注意:析构函数会应用于每个元素,并且所有内存都会被释放。 + +--- + +=== 赋值操作 + +==== 复制赋值 + +```c++ unordered_multiset& operator=(const unordered_multiset& other); ``` + +赋值运算符。拷贝所包含的元素、哈希函数、谓词和最大负载因子,但不拷贝分配器。 + +如果 `Alloc::propagate_on_container_copy_assignment` 存在且 `Alloc::propagate_on_container_copy_assignment::value` 为 `true`,则分配器会被覆盖;否则,拷贝的元素将使用现有的分配器创建。 + +[horizontal] +要求:`value_type` 可拷贝构造。 + +--- + +==== 移动赋值 +```c++unordered_multiset& operator=(unordered_multiset&& other) noexcept(boost::allocator_traits::is_always_equal::value && boost::is_nothrow_move_assignable_v && boost::is_nothrow_move_assignable_v);```移动赋值运算符。 + +如果 `Alloc::propagate_on_container_move_assignment` 存在且 `Alloc::propagate_on_container_move_assignment::value` 为 `true`,则分配器会被覆盖;否则,移动的元素将使用现有的分配器创建。 + +[horizontal] +要求:`value_type` 可移动构造。 + +--- + +==== 初始化列表赋值 +```c++ unordered_multiset& operator=(std::initializer_list il); ``` + +从初始化列表中的值进行赋值。所有现有元素要么被新元素覆盖,要么被销毁。 + +[horizontal] +要求:`value_type` 必须能够从容器中 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^] 并且满足 https://en.cppreference.com/w/cpp/named_req/CopyAssignable[可拷贝赋值^]。 + +--- + +=== 迭代器 + +==== 开始 +```c++ iterator begin() noexcept; const_iterator begin() const noexcept; ``` + +[horizontal] +返回:指向容器第一个元素的迭代器,如果容器为空则返回容器的尾后迭代器。 + +--- + +==== 结束 +```c++ iterator end() noexcept; const_iterator end() const noexcept; ``` + +[horizontal] +返回:指向容器尾后值的迭代器。 + +--- + +==== cbegin +```c++ const_iterator cbegin() const noexcept; ``` + +[horizontal] +返回:指向容器第一个元素的 `const_iterator`,如果容器为空则返回容器的尾后值。 + +--- + +==== cend +```c++ const_iterator cend() const noexcept; ``` + +[horizontal] +返回:指向容器尾后值的 `const_iterator`。 + +--- + +=== 大小与容量 + +==== 空 + +```c++ [[nodiscard]] bool empty() const noexcept; ``` + +[horizontal] +返回:`size() == 0`。 + +--- + +==== 大小 + +```c++ size_type size() const noexcept; ``` + +[horizontal] +返回:`std::distance(begin(), end())`。 + +--- + +==== max_size + +```c++ size_type max_size() const noexcept; ``` + +[horizontal] +返回:可能的最大容器的 `size()`。 + +--- + +=== 修改器 + +==== 原地构造 +```c++ template iterator emplace(Args&&... args); ``` + +插入一个使用参数 `args` 构造的对象到容器中。 + +[horizontal] +要求:`value_type` 必须可以从 `args` 出发在 `X` 中进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^]。 +返回:指向已插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== emplace_hint +```c++ template iterator emplace_hint(const_iterator position, Args&&... args); ``` + +插入一个使用参数 `args` 构造的对象到容器中。 + +`hint` 是关于元素插入位置的建议。 + +[horizontal] +要求:`value_type` 必须可以从 `args` 出发在 `X` 中进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^]。 +返回:指向已插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:标准关于 `hint` 的含义表述相当模糊。但唯一实际的使用方式,也是 Boost.Unordered 支持的唯一方式,是将其指向一个具有相同键的已存在元素。 +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 复制插入 +```c++ iterator insert(const value_type& obj); ``` + +将 `obj` 插入容器中。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 +返回:指向已插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 移动插入 +```c++ iterator insert(value_type&& obj); ``` + +将 `obj` 插入容器中。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^]。 +返回:指向已插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 带提示的复制插入 +```c++ iterator insert(const_iterator hint, const value_type& obj); ``` + +将 `obj` 插入容器中。 + +`hint` 是关于元素插入位置的建议。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 +返回:指向已插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:标准关于 `hint` 的含义表述相当模糊。但唯一实际的使用方式,也是 Boost.Unordered 支持的唯一方式,是将其指向一个具有相同键的已存在元素。 +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 带提示的移动插入 +```c++ iterator insert(const_iterator hint, value_type&& obj); ``` + +将 `obj` 插入容器中。 + +`hint` 是关于元素插入位置的建议。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^]。 +返回:指向已插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:标准关于 `hint` 的含义表述相当模糊。但唯一实际的使用方式,也是 Boost.Unordered 支持的唯一方式,是将其指向一个具有相同键的已存在元素。 +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 迭代器范围插入 +```c++ template void insert(InputIterator first, InputIterator last); ``` + +将一个范围内的元素插入容器中。 + +[horizontal] +要求:`value_type` 必须可以从 `*first` 出发在 `X` 中进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^]。 +抛出:当插入单个元素时,如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 初始化列表插入 +```c++ void insert(std::initializer_list il); ``` + +将一个范围内的元素插入容器中。当且仅当容器中没有具有等价键的元素时,才会插入这些元素。 + +[horizontal] +要求:`value_type` 可以在容器中进行 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 +抛出:当插入单个元素时,如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 + +--- + +==== 通过迭代器提取 +```c++ node_type extract(const_iterator position); ``` + +移除 `position` 所指向的元素。 + +[horizontal] +返回:一个拥有该元素的 `node_type`。 +注意:通过此方法提取的节点可以插入到兼容的 `unordered_set` 中。 + +--- + +==== 按值提取 +```c++ node_type extract(const key_type& k); template node_type extract(K&& k); ``` + +移除一个键与 `k` 等价的元素。 + +[horizontal] +返回:如果找到该元素,则返回一个拥有该元素的 `node_type`;否则返回一个空的 `node_type`。 +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +注意:通过此方法提取的节点可以插入到兼容的 `unordered_set` 中。 + +`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 都不能从 `K` 隐式转换时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 通过 `node_handle` 插入 +```c++ iterator insert(node_type&& nh); ``` + +如果 `nh` 为空,则无效果。 + +否则,插入 `nh` 所拥有的元素。 + +[horizontal] +要求:`nh` 为空,或者 `nh.get_allocator()` 等于容器的分配器。 +返回:如果 `nh` 为空,则返回 `end()`。 +否则,返回指向新插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 指向元素的指针和引用永远不会失效。 +此函数可用于插入从兼容的 `unordered_set` 中提取的节点。 + +--- + +==== 带提示和 `node_handle` 的插入 +```c++ iterator insert(const_iterator hint, node_type&& nh); ``` + +如果 `nh` 为空,则无效果。 + +否则,插入 `nh` 所拥有的元素。 + +`hint` 是关于元素插入位置的建议。 + +[horizontal] +要求:`nh` 为空,或者 `nh.get_allocator()` 等于容器的分配器。 +返回:如果 `nh` 为空,则返回 `end()`。 +否则,返回指向新插入元素的迭代器。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:标准关于 `hint` 的含义表述相当模糊。但唯一实际的使用方式,也是 Boost.Unordered 支持的唯一方式,是将其指向一个具有相同键的已存在元素。 +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。 +指向元素的指针和引用永远不会失效。 +此函数可用于插入从兼容的 `unordered_set` 中提取的节点。 + +--- + +==== 通过位置擦除 + +```c++ iterator erase(iterator position); iterator erase(const_iterator position); ``` + +擦除 `position` 所指向的元素。 + +[horizontal] +返回:`position` 在被擦除之前的后一个迭代器。 +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +注意:在旧版本中,此操作可能效率较低,因为它需要搜索多个桶以找到返回迭代器的位置。数据结构已经更改,不再是这种情况,并且备用的擦除方法已被弃用。 + +--- + +==== 通过值擦除 +```c++ size_type erase(const key_type& k); template size_type erase(K&& x); ``` + +擦除所有键与 `k` 等价的元素。 + +[horizontal] +返回:被擦除的元素数量。 +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 都不能从 `K` 隐式转换时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 范围擦除 + +```c++ iterator erase(const_iterator first, const_iterator last); ``` + +擦除从 `first` 到 `last` 范围内的元素。 + +[horizontal] +返回:被擦除元素之后的迭代器——即 `last`。 +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +在此实现中,此重载不会调用任何函数对象的方法,因此不会抛出异常,但这在其他实现中可能并不成立。 + +--- + +==== quick_erase +```c++ void quick_erase(const_iterator position); ``` + +擦除 `position` 所指向的元素。 + +[horizontal] +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +在此实现中,此重载不会调用任何函数对象的方法,因此不会抛出异常,但这在其他实现中可能并不成立。 +注意:引入此方法是因为从 `erase` 返回指向下一个元素的迭代器成本较高,但容器已经过重新设计,不再存在这种情况。因此,此方法现已弃用。 + +--- + +==== erase_return_void +```c++ void erase_return_void(const_iterator position); ``` + +擦除 `position` 所指向的元素。 + +[horizontal] +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +在此实现中,此重载不会调用任何函数对象的方法,因此不会抛出异常,但这在其他实现中可能并不成立。 +注意:引入此方法是因为从 `erase` 返回指向下一个元素的迭代器成本较高,但容器已经过重新设计,不再存在这种情况。因此,此方法现已弃用。 + +--- + +==== 交换 +```c++ void swap(unordered_multiset&) noexcept(boost::allocator_traits::is_always_equal::value && boost::is_nothrow_swappable_v && boost::is_nothrow_swappable_v); ``` + +交换容器的内容与参数的内容。 + +如果 `Allocator::propagate_on_container_swap` 被声明且 `Allocator::propagate_on_container_swap::value` 为 `true`,则交换容器的分配器。否则,使用不相等的分配器进行交换将导致未定义行为。 + +[horizontal] +抛出:除非 `key_equal` 或 `hasher` 的拷贝构造函数或拷贝赋值运算符抛出异常,否则不抛出任何异常。 +注意:异常规范与 C++11 标准不完全相同,因为相等谓词和哈希函数是使用其拷贝构造函数进行交换的。 + +--- + +==== 清空 +```c++ void clear() noexcept; ``` + +擦除容器中的所有元素。 + +[horizontal] +后置条件:`size() == 0` 抛出:永不抛出异常。 + +--- + +==== 合并 +```c++ template void merge(unordered_multiset& source); template void merge(unordered_multiset&& source); template void merge(unordered_set& source); template void merge(unordered_set&& source); ``` + +尝试“合并”两个容器:遍历 `source`,提取 `source` 中的所有节点,并将它们插入到 `*this` 中。 + +由于 `source` 可能具有不同的哈希函数和键相等谓词,因此 `source` 中每个节点的键都会使用 `this->hash_function()` 重新计算哈希值,然后在必要时使用 `this->key_eq()` 进行比较。 + +如果 `this->get_allocator() != source.get_allocator()`,则此函数的行为未定义。 + +此函数不会拷贝或移动任何元素,而是仅仅将节点从 `source` 重新定位到 `*this` 中。 + +[horizontal] +注意:;; + -- +* 指向被转移元素的指针和引用保持有效。 +* 使指向被转移元素的迭代器失效。 +* 使属于 `*this` 的迭代器失效。 +* 指向 `source` 中未被转移元素的迭代器保持有效。 +-- + +--- + +=== 观察器 + +==== get_allocator +``` allocator_type get_allocator() const noexcept; ``` + +--- + +==== 哈希函数 +``` hasher hash_function() const; ``` + +[horizontal] +返回:容器的哈希函数对象。 + +--- + +==== key_eq + +``` key_equal key_eq() const; ``` + +[horizontal] +返回:容器的键相等谓词。 + +--- + +=== 查找 + +==== find +```c++ iterator find(const key_type& k); const_iterator find(const key_type& k) const; template iterator find(const K& k); template const_iterator find(const K& k) const; template iterator find(CompatibleKey const&, CompatibleHash const&, CompatiblePredicate const&); template const_iterator find(CompatibleKey const&, CompatibleHash const&, CompatiblePredicate const&) const; ``` + +[horizontal] +返回:指向键与 `k` 等价的元素的迭代器;如果不存在这样的元素,则返回 `b.end()`。 +注意:包含 `CompatibleKey`、`CompatibleHash` 和 `CompatiblePredicate` 的模板化重载是非标准扩展,允许您为不同类型的键使用兼容的哈希函数和相等谓词,以避免昂贵的类型转换。通常不鼓励使用它们,而应使用 `K` 成员函数模板。 + +`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== count +```c++ size_type count(const key_type& k) const; template size_type count(const K& k) const; ``` + +[horizontal] +返回:键与 `k` 等价的元素数量。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 包含 +```c++ bool contains(const key_type& k) const; template bool contains(const K& k) const; ``` + +[horizontal] +返回:一个布尔值,指示容器中是否存在键等于 `key` 的元素。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== equal_range +```c++ std::pair equal_range(const key_type& k); std::pair equal_range(const key_type& k) const; template std::pair equal_range(const K& k); template std::pair equal_range(const K& k) const; ``` + +[horizontal] +返回:包含所有键与 `k` 等价的元素的范围。如果容器不包含任何此类元素,则返回 `std::make_pair(b.end(), b.end())`。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +=== 桶接口 + +==== bucket_count +```c++ size_type bucket_count() const noexcept; ``` + +[horizontal] +返回:桶的数量。 + +--- + +==== max_bucket_count +```c++ size_type max_bucket_count() const noexcept; ``` + +[horizontal] +返回:桶数量的上界。 + +--- + +==== 桶大小 +```c++ size_type bucket_size(size_type n) const; ``` + +[horizontal] +Requires:;; `n < bucket_count()` Returns:;; The number of elements in bucket `n`. + +--- + +==== 桶 +```c++ size_type bucket(const key_type& k) const; template size_type bucket(const K& k) const; ``` + +[horizontal] +返回:将包含键为 `k` 的元素的桶的索引。 +后置条件:返回值小于 `bucket_count()`。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 开始 + +```c++ local_iterator begin(size_type n); const_local_iterator begin(size_type n) const; ``` + +[horizontal] +要求:`n` 必须在 `[0, bucket_count())` 范围内。 +返回:指向索引为 `n` 的桶中第一个元素的局部迭代器。 + +--- + +==== 结束 +```c++ local_iterator end(size_type n); const_local_iterator end(size_type n) const; ``` + +[horizontal] +要求:`n` 必须在 `[0, bucket_count())` 范围内。 +返回:指向索引为 `n` 的桶中“尾后”元素的局部迭代器。 + +--- + +==== cbegin +```c++ const_local_iterator cbegin(size_type n) const; ``` + +[horizontal] +要求:`n` 必须在 `[0, bucket_count())` 范围内。 +返回:指向索引为 `n` 的桶中第一个元素的常量局部迭代器。 + +--- + +==== cend +```c++ const_local_iterator cend(size_type n) const; ``` + +[horizontal] +要求:`n` 必须在 `[0, bucket_count())` 范围内。 +返回:指向索引为 `n` 的桶中“尾后”元素的常量局部迭代器。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ float load_factor() const noexcept; ``` + +[horizontal] +返回:每个桶的平均元素数量。 + +--- + +==== 最大负载因子 + +```c++ float max_load_factor() const noexcept; ``` + +[horizontal] +返回:当前最大负载因子。 + +--- + +==== 设置最大负载因子 +```c++ void max_load_factor(float z); ``` + +[horizontal] +效果:使用 `z` 作为提示,更改容器的最大负载因子。 + +--- + +==== 重哈希 +```c++ void rehash(size_type n); ``` + +更改桶的数量,使其至少包含 `n` 个桶,并且使得负载因子小于或等于最大负载因子。在适用的情况下,这将增大或缩小与容器关联的 `bucket_count()`。 + +当 `size() == 0` 时,`rehash(0)` 将释放底层的桶数组。 + +使迭代器失效,并改变元素的顺序。指向元素的指针和引用不会失效。 + +[horizontal] +当 `size() == 0` 时, `rehash(0)` 将释放底层桶数组。 + +--- + +==== 保留 +```c++ void reserve(size_type n); ``` + +等价于 `a.rehash(ceil(n / a.max_load_factor()))`;如果 `n > 0` 且 `a.max_load_factor() == std::numeric_limits::infinity()`,则等价于 `a.rehash(1)`。 + +与 `rehash` 类似,此函数可用于增大或缩小容器中的桶数量。 + +使迭代器失效,并改变元素的顺序。指向元素的指针和引用不会失效。 + +[horizontal] +当 `size() == 0` 时, `rehash(0)` 将释放底层桶数组。 + + +--- + +=== 推导指引 +如果满足以下任一条件,则推导指引不会参与重载决议: + +- 它具有一个 `InputIterator` 模板参数,并且为该参数推导出的类型不符合输入迭代器的要求。 +- 它具有一个 `Allocator` 模板参数,并且为该参数推导出的类型不符合分配器的要求。 +- 它具有一个 `Hash` 模板参数,并且为该参数推导出的类型是整数类型或符合分配器的要求。 +- 它具有一个 `Pred` 模板参数,并且为该参数推导出的类型符合分配器的要求。 + +推导指引中的 `size_type` 参数类型指的是由该推导指引所推导出的容器类型的 `size_type` 成员类型。其默认值与所选构造函数的默认值一致。 + +==== _iter-value-type_ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-value-type__ = + typename std::iterator_traits::value_type; // exposition only +----- + +=== 相等性比较 + +==== 运算符 +```c++ template bool operator==(const unordered_multiset& x, const unordered_multiset& y); ``` + +若 `x.size() == y.size()` 且对于 `x` 中的每个元素,在 `y` 中均存在一个具有相同键且值相等(使用 `operator==` 比较值类型)的元素,则返回 `true`。 + +[horizontal] +注意:如果两个容器不具有等价的相等谓词,则行为未定义。 + +--- + +==== operator! +```c++ template bool operator!=(const unordered_multiset& x, const unordered_multiset& y); ``` + +若 `x.size() == y.size()` 且对于 `x` 中的每个元素,在 `y` 中均存在一个具有相同键且值相等(使用 `operator==` 比较值类型)的元素,则返回 `false`。 + +[horizontal] +注意:如果两个容器不具有等价的相等谓词,则行为未定义。 + +--- + +=== 交换 +```c++ template void swap(unordered_multiset& x, unordered_multiset& y) noexcept(noexcept(x.swap(y))); ``` + +交换 `x` 和 `y` 的内容。 + +如果 `Allocator::propagate_on_container_swap` 被声明且 `Allocator::propagate_on_container_swap::value` 为 `true`,则交换容器的分配器。否则,使用不相等的分配器进行交换将导致未定义行为。 + +[horizontal] +效果:`x.swap(y)` +抛出:除非 `key_equal` 或 `hasher` 的拷贝构造函数或拷贝赋值运算符抛出异常,否则不抛出任何异常。 +注意:异常规范与 C++11 标准不完全相同,因为相等谓词和哈希函数是使用其拷贝构造函数进行交换的。 + +--- + +=== erase_if +```c++ template typename unordered_multiset::size_type erase_if(unordered_multiset& c, Predicate pred); ``` + +遍历容器 `c`,并移除所有使给定谓词返回 `true` 的元素。 + +[horizontal] +返回:被擦除的元素数量。 注意:等价于: ```c++auto original_size = c.size();for (auto i = c.begin(), last = c.end(); i != last; ) { if (pred(*i)) { i = c.erase(i); } else { ++i; }}return original_size - c.size();``` + +=== 序列化 + +`unordered_multiset` 可以通过本库提供的 API,借助 link:../../../../../serialization/index.html[Boost.Serialization^] 进行归档/恢复。支持常规归档和 XML 归档。 + +==== 保存 unordered_multiset 到归档 + +将 `unordered_multiset` 容器 `x` 的所有元素保存到归档(XML 归档)`ar` 中。 + +[horizontal] +要求:`value_type` 必须是可序列化的(对于 XML 归档需支持 XML 序列化),并且支持 Boost.Serialization 的 `save_construct_data`/`load_construct_data` 协议(该协议由满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 的类型自动支持)。 + +--- + +==== 从归档加载 unordered_multiset + +删除 `unordered_multiset` 容器 `x` 中所有已存在的元素,并从归档(XML 归档)`ar` 中插入从原始 `unordered_multiset` 容器 `other` 保存到 `ar` 所读取存储中的元素恢复出的副本。 + +[horizontal] +要求:`value_type` 必须满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^]。`x.key_equal()` 在功能上必须等价于 `other.key_equal()`。 +注意:如果归档是使用 Boost 1.84 之前版本的 Boost 保存的,则必须全局定义配置宏 `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0` 才能使此操作成功;否则,将抛出异常。 + +--- + +==== 将迭代器/常量迭代器保存到归档 + +将迭代器(或常量迭代器)`it` 的位置信息保存到归档(XML 归档)`ar` 中。`it` 可以是一个 `end()` 迭代器。 + +[horizontal] +要求:`it` 所指向的 `unordered_multiset` 容器 `x` 必须先被保存到 `ar` 中,并且在保存 `x` 和保存 `it` 之间,不能对 `x` 执行任何修改操作。 + +--- + +==== 从归档加载迭代器/常量迭代器 + +使迭代器(或常量迭代器)`it` 指向原始迭代器(或常量迭代器)被保存到归档(XML 归档)`ar` 所读取存储中的位置恢复后的位置。 + +[horizontal] +要求;; 若 `x` 是 `it` 所指向的 `unordered_multiset` 容器,则在加载 `x` 与加载 `it` 期间不得对 `x` 执行任何修改操作。 diff --git a/doc/modules/ROOT/pages/reference/unordered_node_map_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/unordered_node_map_zh_Hans.adoc new file mode 100644 index 0000000..ec33e75 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/unordered_node_map_zh_Hans.adoc @@ -0,0 +1,1399 @@ +[#unordered_node_map] +== 类模板 unordered_node_map + +:idprefix: unordered_node_map_ + +`boost::unordered_node_map` — 一个基于节点的、开放寻址的无序关联容器,将唯一键与对应值相关联。 + +`boost::unordered_node_map` 使用与 `boost::unordered_flat_map` 类似的开放寻址布局,但由于其基于节点的特性,它提供了指针稳定性和节点处理功能。其性能介于 `boost::unordered_map` 和 `boost::unordered_flat_map` 之间。 + +由于使用开放寻址,`boost::unordered_node_map` 的接口在多个方面与 `boost::unordered_map`/`std::unordered_map` 有所不同: + +- `begin()` 不是常数时间复杂度操作。 - 未提供用于桶管理的 API(除 `bucket_count` 外)。 - 容器的最大负载因子由内部管理,用户无法进行设置。 + +除此之外,`boost::unordered_node_map` 基本上可以作为标准无序关联容器的即时代替品。 + +=== 概要 + +[listing,subs="+macros,+quotes"] +----- +// #include xref:reference/header_unordered_node_map.adoc[``] + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + class unordered_node_map { + public: + // types + using key_type = Key; + using mapped_type = T; + using value_type = std::pair; + using init_type = std::pair< + typename std::remove_const::type, + typename std::remove_const::type + >; + using hasher = Hash; + using key_equal = Pred; + using allocator_type = Allocator; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + using iterator = _implementation-defined_; + using const_iterator = _implementation-defined_; + + using node_type = _implementation-defined_; + using insert_return_type = _implementation-defined_; + + using stats = xref:reference/stats.adoc#stats_stats_type[__stats-type__]; // if statistics are xref:unordered_node_map_boost_unordered_enable_stats[enabled] + + // construct/copy/destroy + xref:#unordered_node_map_default_constructor[unordered_node_map](); + explicit xref:#unordered_node_map_bucket_count_constructor[unordered_node_map](size_type n, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + template + xref:#unordered_node_map_iterator_range_constructor[unordered_node_map](InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#unordered_node_map_copy_constructor[unordered_node_map](const unordered_node_map& other); + xref:#unordered_node_map_move_constructor[unordered_node_map](unordered_node_map&& other); + template + xref:#unordered_node_map_iterator_range_constructor_with_allocator[unordered_node_map](InputIterator f, InputIterator l, const allocator_type& a); + explicit xref:#unordered_node_map_allocator_constructor[unordered_node_map](const Allocator& a); + xref:#unordered_node_map_copy_constructor_with_allocator[unordered_node_map](const unordered_node_map& other, const Allocator& a); + xref:#unordered_node_map_move_constructor_with_allocator[unordered_node_map](unordered_node_map&& other, const Allocator& a); + xref:#unordered_node_map_move_constructor_from_concurrent_node_map[unordered_node_map](concurrent_node_map&& other); + xref:#unordered_node_map_initializer_list_constructor[unordered_node_map](std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#unordered_node_map_bucket_count_constructor_with_allocator[unordered_node_map](size_type n, const allocator_type& a); + xref:#unordered_node_map_bucket_count_constructor_with_hasher_and_allocator[unordered_node_map](size_type n, const hasher& hf, const allocator_type& a); + template + xref:#unordered_node_map_iterator_range_constructor_with_bucket_count_and_allocator[unordered_node_map](InputIterator f, InputIterator l, size_type n, const allocator_type& a); + template + xref:#unordered_node_map_iterator_range_constructor_with_bucket_count_and_hasher[unordered_node_map](InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); + xref:#unordered_node_map_initializer_list_constructor_with_allocator[unordered_node_map](std::initializer_list il, const allocator_type& a); + xref:#unordered_node_map_initializer_list_constructor_with_bucket_count_and_allocator[unordered_node_map](std::initializer_list il, size_type n, + const allocator_type& a); + xref:#unordered_node_map_initializer_list_constructor_with_bucket_count_and_hasher_and_allocator[unordered_node_map](std::initializer_list il, size_type n, const hasher& hf, + const allocator_type& a); + xref:#unordered_node_map_destructor[~unordered_node_map](); + unordered_node_map& xref:#unordered_node_map_copy_assignment[operator++=++](const unordered_node_map& other); + unordered_node_map& xref:#unordered_node_map_move_assignment[operator++=++](unordered_node_map&& other) ++noexcept( + (boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_move_assignment::value) && + std::is_same::value);++ + unordered_node_map& xref:#unordered_node_map_initializer_list_assignment[operator++=++](std::initializer_list); + allocator_type xref:#unordered_node_map_get_allocator[get_allocator]() const noexcept; + + // iterators + iterator xref:#unordered_node_map_begin[begin]() noexcept; + const_iterator xref:#unordered_node_map_begin[begin]() const noexcept; + iterator xref:#unordered_node_map_end[end]() noexcept; + const_iterator xref:#unordered_node_map_end[end]() const noexcept; + const_iterator xref:#unordered_node_map_cbegin[cbegin]() const noexcept; + const_iterator xref:#unordered_node_map_cend[cend]() const noexcept; + + // capacity + ++[[nodiscard]]++ bool xref:#unordered_node_map_empty[empty]() const noexcept; + size_type xref:#unordered_node_map_size[size]() const noexcept; + size_type xref:#unordered_node_map_max_size[max_size]() const noexcept; + + // modifiers + template std::pair xref:#unordered_node_map_emplace[emplace](Args&&... args); + template iterator xref:#unordered_node_map_emplace_hint[emplace_hint](const_iterator position, Args&&... args); + std::pair xref:#unordered_node_map_copy_insert[insert](const value_type& obj); + std::pair xref:#unordered_node_map_copy_insert[insert](const init_type& obj); + std::pair xref:#unordered_node_map_move_insert[insert](value_type&& obj); + std::pair xref:#unordered_node_map_move_insert[insert](init_type&& obj); + iterator xref:#unordered_node_map_copy_insert_with_hint[insert](const_iterator hint, const value_type& obj); + iterator xref:#unordered_node_map_copy_insert_with_hint[insert](const_iterator hint, const init_type& obj); + iterator xref:#unordered_node_map_move_insert_with_hint[insert](const_iterator hint, value_type&& obj); + iterator xref:#unordered_node_map_copy_insert_with_hint[insert](const_iterator hint, init_type&& obj); + template void xref:#unordered_node_map_insert_iterator_range[insert](InputIterator first, InputIterator last); + void xref:#unordered_node_map_insert_initializer_list[insert](std::initializer_list); + insert_return_type xref:#unordered_node_map_insert_node[insert](node_type&& nh); + iterator xref:#unordered_node_map_insert_node_with_hint[insert](const_iterator hint, node_type&& nh); + + template + std::pair xref:#unordered_node_map_try_emplace[try_emplace](const key_type& k, Args&&... args); + template + std::pair xref:#unordered_node_map_try_emplace[try_emplace](key_type&& k, Args&&... args); + template + std::pair xref:#unordered_node_map_try_emplace[try_emplace](K&& k, Args&&... args); + template + iterator xref:#unordered_node_map_try_emplace_with_hint[try_emplace](const_iterator hint, const key_type& k, Args&&... args); + template + iterator xref:#unordered_node_map_try_emplace_with_hint[try_emplace](const_iterator hint, key_type&& k, Args&&... args); + template + iterator xref:#unordered_node_map_try_emplace_with_hint[try_emplace](const_iterator hint, K&& k, Args&&... args); + template + std::pair xref:#unordered_node_map_insert_or_assign[insert_or_assign](const key_type& k, M&& obj); + template + std::pair xref:#unordered_node_map_insert_or_assign[insert_or_assign](key_type&& k, M&& obj); + template + std::pair xref:#unordered_node_map_insert_or_assign[insert_or_assign](K&& k, M&& obj); + template + iterator xref:#unordered_node_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, const key_type& k, M&& obj); + template + iterator xref:#unordered_node_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, key_type&& k, M&& obj); + template + iterator xref:#unordered_node_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, K&& k, M&& obj); + + _convertible-to-iterator_ xref:#unordered_node_map_erase_by_position[erase](iterator position); + _convertible-to-iterator_ xref:#unordered_node_map_erase_by_position[erase](const_iterator position); + size_type xref:#unordered_node_map_erase_by_key[erase](const key_type& k); + template size_type xref:#unordered_node_map_erase_by_key[erase](K&& k); + iterator xref:#unordered_node_map_erase_range[erase](const_iterator first, const_iterator last); + void xref:#unordered_node_map_swap[swap](unordered_node_map& other) + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_swap::value); + node_type xref:#unordered_node_map_extract_by_position[extract](const_iterator position); + node_type xref:#unordered_node_map_extract_by_key[extract](const key_type& key); + template node_type xref:#unordered_node_map_extract_by_key[extract](K&& key); + init_type xref:#unordered_node_map_pull[pull](const_iterator position); + void xref:#unordered_node_map_clear[clear]() noexcept; + + template + void xref:#unordered_node_map_merge[merge](unordered_node_map& source); + template + void xref:#unordered_node_map_merge[merge](unordered_node_map&& source); + + // observers + hasher xref:#unordered_node_map_hash_function[hash_function]() const; + key_equal xref:#unordered_node_map_key_eq[key_eq]() const; + + // map operations + iterator xref:#unordered_node_map_find[find](const key_type& k); + const_iterator xref:#unordered_node_map_find[find](const key_type& k) const; + template + iterator xref:#unordered_node_map_find[find](const K& k); + template + const_iterator xref:#unordered_node_map_find[find](const K& k) const; + size_type xref:#unordered_node_map_count[count](const key_type& k) const; + template + size_type xref:#unordered_node_map_count[count](const K& k) const; + bool xref:#unordered_node_map_contains[contains](const key_type& k) const; + template + bool xref:#unordered_node_map_contains[contains](const K& k) const; + std::pair xref:#unordered_node_map_equal_range[equal_range](const key_type& k); + std::pair xref:#unordered_node_map_equal_range[equal_range](const key_type& k) const; + template + std::pair xref:#unordered_node_map_equal_range[equal_range](const K& k); + template + std::pair xref:#unordered_node_map_equal_range[equal_range](const K& k) const; + + // element access + mapped_type& xref:#unordered_node_map_operator[operator[+]+](const key_type& k); + mapped_type& xref:#unordered_node_map_operator[operator[+]+](key_type&& k); + template mapped_type& xref:#unordered_node_map_operator[operator[+]+](K&& k); + mapped_type& xref:#unordered_node_map_at[at](const key_type& k); + const mapped_type& xref:#unordered_node_map_at[at](const key_type& k) const; + template mapped_type& xref:#unordered_node_map_at[at](const K& k); + template const mapped_type& xref:#unordered_node_map_at[at](const K& k) const; + + // bucket interface + size_type xref:#unordered_node_map_bucket_count[bucket_count]() const noexcept; + + // hash policy + float xref:#unordered_node_map_load_factor[load_factor]() const noexcept; + float xref:#unordered_node_map_max_load_factor[max_load_factor]() const noexcept; + void xref:#unordered_node_map_set_max_load_factor[max_load_factor](float z); + size_type xref:#unordered_node_map_max_load[max_load]() const noexcept; + void xref:#unordered_node_map_rehash[rehash](size_type n); + void xref:#unordered_node_map_reserve[reserve](size_type n); + + // statistics (if xref:unordered_node_map_boost_unordered_enable_stats[enabled]) + stats xref:#unordered_node_map_get_stats[get_stats]() const; + void xref:#unordered_node_map_reset_stats[reset_stats]() noexcept; + }; + + // Deduction Guides + template>, + class Pred = std::equal_to>, + class Allocator = std::allocator>> + unordered_node_map(InputIterator, InputIterator, typename xref:#unordered_node_map_deduction_guides[__see below__]::size_type = xref:#unordered_node_map_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_node_map, xref:#unordered_node_map_iter_mapped_type[__iter-mapped-type__], Hash, + Pred, Allocator>; + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + unordered_node_map(std::initializer_list>, + typename xref:#unordered_node_map_deduction_guides[__see below__]::size_type = xref:#unordered_node_map_deduction_guides[__see below__], Hash = Hash(), + Pred = Pred(), Allocator = Allocator()) + -> unordered_node_map; + + template + unordered_node_map(InputIterator, InputIterator, typename xref:#unordered_node_map_deduction_guides[__see below__]::size_type, Allocator) + -> unordered_node_map, xref:#unordered_node_map_iter_mapped_type[__iter-mapped-type__], + boost::hash>, + std::equal_to>, Allocator>; + + template + unordered_node_map(InputIterator, InputIterator, Allocator) + -> unordered_node_map, xref:#unordered_node_map_iter_mapped_type[__iter-mapped-type__], + boost::hash>, + std::equal_to>, Allocator>; + + template + unordered_node_map(InputIterator, InputIterator, typename xref:#unordered_node_map_deduction_guides[__see below__]::size_type, Hash, + Allocator) + -> unordered_node_map, xref:#unordered_node_map_iter_mapped_type[__iter-mapped-type__], Hash, + std::equal_to>, Allocator>; + + template + unordered_node_map(std::initializer_list>, typename xref:#unordered_node_map_deduction_guides[__see below__]::size_type, + Allocator) + -> unordered_node_map, std::equal_to, Allocator>; + + template + unordered_node_map(std::initializer_list>, Allocator) + -> unordered_node_map, std::equal_to, Allocator>; + + template + unordered_node_map(std::initializer_list>, typename xref:#unordered_node_map_deduction_guides[__see below__]::size_type, + Hash, Allocator) + -> unordered_node_map, Allocator>; + +} // namespace unordered +} // namespace boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_Key_ +.2+|`std::pair` must be https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^] +从任何可转换为 `std::pair` 的对象构造并插入容器,并且还必须可以从容器中 https://en.cppreference.com/w/cpp/named_req/Erasable[可擦除^]。 + +|_T_ + +|_Hash_ +|A unary function object type that acts a hash function for a `Key`. It takes a single argument of type `Key` and returns a value of type `std::size_t`. + +|_Pred_ +|A binary function object that induces an equivalence relation on values of type `Key`. It takes two arguments of type `Key` and returns a value of type `bool`. + +|_Allocator_ +|An allocator whose value type is the same as the container's value type. +支持使用 https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[异形指针] 的分配器。 + +|=== + +容器的元素节点被保存在内部的 _桶数组_ 中。节点根据其元素的哈希码被插入到对应的桶中,但如果该桶已被占用(发生 _冲突_),则会使用原始位置附近的一个可用桶。 + +桶数组的大小可以通过调用 `insert`/`emplace` 自动增加,或者作为调用 `rehash`/`reserve` 的结果。容器的 _负载因子_(元素数量除以桶数量)永远不会大于 `max_load_factor()`,但在容器规模较小时,实现可能会允许更高的负载。 + +如果 `link:../../../../../container_hash/doc/html/hash.html#ref_hash_is_avalanching[hash_is_avalanching]::value` 为 `true`,则哈希函数将按原样使用;否则,会增加一个位混合后处理阶段,以牺牲额外的计算成本为代价来提高哈希质量。 + +--- + +=== 配置宏 + +==== `BOOST_UNORDERED_ENABLE_STATS` + +全局定义此宏可为容器启用 xref:reference/stats.adoc#stats[统计计算]。请注意,此选项会降低许多操作的整体性能。 + +--- + +=== 类型定义 + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ iterator; +---- + +一种迭代器,其值类型为 `value_type` 。 + +迭代器类别至少为前向迭代器。 + +可转换为 `const_iterator`。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ const_iterator; +---- + +一个常量迭代器,其值类型为 `value_type` 。 + +迭代器类别至少为前向迭代器。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ node_type; +---- + +一个用于存放被提取容器元素的类,符合 https://en.cppreference.com/w/cpp/container/node_handle[NodeHandle] 模型。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ insert_return_type; +---- + +内部类模板的特化: + +[source,c++,subs=+quotes] +---- +template +struct _insert_return_type_ // name is exposition only +{ + Iterator position; + bool inserted; + NodeType node; +}; +---- + +其中 `Iterator` = `iterator`,`NodeType` = `node_type`。 + +--- + +=== 构造函数 + +==== 默认构造函数 +```c++ unordered_node_map(); ``` + +使用 `hasher()` 作为哈希函数,`key_equal()` 作为键相等谓词,`allocator_type()` 作为分配器,构造一个空容器。 + +[horizontal] +后置条件:`size() == 0` +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 桶数构造函数 +```c++ explicit unordered_node_map(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器。 + +[horizontal] +后置条件:`size() == 0` +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_node_map(InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 复制构造函数 +```c++ unordered_node_map(unordered_node_map const& other); ``` + +拷贝构造函数。拷贝所包含的元素、哈希函数、谓词和分配器。 + +如果 `Allocator::select_on_container_copy_construction` 存在且具有正确的签名,则分配器将根据其结果进行构造。 + +--- + +==== 移动构造函数 +```c++ unordered_node_map(unordered_node_map&& other); ``` + +移动构造函数。`other` 的内部桶数组会被直接转移给新容器。哈希函数、谓词和分配器通过移动构造从 `other` 获得。如果启用了统计信息(详见 xref:unordered_node_map_boost_unordered_enable_stats[`BOOST_UNORDERED_ENABLE_STATS`]),则会从 `other` 转移内部的统计信息,并调用 `other.reset_stats()`。 + +--- + +==== 带分配器的迭代器范围构造函数 +```c++ template unordered_node_map(InputIterator f, InputIterator l, const allocator_type& a); ``` + +使用 `a` 作为分配器,以默认哈希函数和键相等谓词构造一个空容器,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:`hasher`、`key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 分配器构造函数 +```c++ explicit unordered_node_map(Allocator const& a); ``` + +使用分配器 `a` 构造一个空容器。 + +--- + +==== 带分配器的拷贝构造函数 +```c++ unordered_node_map(unordered_node_map const& other, Allocator const& a); ``` + +构造一个容器,拷贝 `other` 所包含的元素、哈希函数和谓词,但使用分配器 `a`。 + +--- + +==== 带分配器的移动构造函数 +```c++ unordered_node_map(unordered_node_map&& other, Allocator const& a); ``` + +如果 `a == other.get_allocator()`,则 `other` 的元素节点会直接转移给新容器;否则,元素将从 `other` 中的元素通过移动构造获得。哈希函数和谓词通过移动构造从 `other` 获得,而分配器则通过拷贝构造从 `a` 获得。如果启用了统计信息(详见 xref:unordered_node_map_boost_unordered_enable_stats[`BOOST_UNORDERED_ENABLE_STATS`]),则当 `a == other.get_allocator()` 时,会从 `other` 转移内部的统计信息,并且始终会调用 `other.reset_stats()`。 + +--- + +==== 从 `concurrent_node_map` 的移动构造函数 + +```c++ unordered_node_map(concurrent_node_map&& other); ``` + +从 xref:#concurrent_node_map[`concurrent_node_map`] 进行移动构造。`other` 的内部桶数组直接转移给新容器。哈希函数、谓词和分配器通过移动构造从 `other` 获得。如果启用了统计信息(详见 xref:unordered_node_map_boost_unordered_enable_stats[`BOOST_UNORDERED_ENABLE_STATS`]),则从 `other` 转移内部的统计信息,并调用 `other.reset_stats()`。 + +[horizontal] +复杂度:常数时间。 +并发性:在 `other` 上阻塞。 + +--- + +==== 初始化列表构造函数 +[source,c++,subs="+quotes"] +---- +unordered_node_map(std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`eql` 作为键相等谓词和 `a`,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带分配器的桶数构造函数 +```c++ unordered_node_map(size_type n, allocator_type const& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,使用默认的哈希函数和键相等谓词,并以 `a` 作为分配器。 + +[horizontal] +后置条件:`size() == 0` +要求:`hasher` 和 `key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带哈希函数和分配器的桶数构造函数 +```c++ unordered_node_map(size_type n, hasher const& hf, allocator_type const& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,使用默认的键相等谓词,并以 `a` 作为分配器。 + +[horizontal] +后置条件:`size() == 0` 要求:`key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 要求。 + +--- + +==== 带桶数和分配器的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_node_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 作为分配器以及默认的哈希函数和键相等谓词,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:`hasher`、`key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数和哈希函数的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- + template + unordered_node_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`a` 作为分配器,使用默认的键相等谓词,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求;; `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带分配器的初始化列表构造函数 + +```c++ unordered_node_map(std::initializer_list il, const allocator_type& a); ``` + +使用 `a` 以及默认的哈希函数和键相等谓词构造一个空容器,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数和分配器的初始化列表构造函数 + +```c++ unordered_node_map(std::initializer_list il, size_type n, const allocator_type& a); ``` + +使用 `a` 以及默认的哈希函数和键相等谓词,构造一个至少包含 `n` 个桶的空容器,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数、哈希函数和分配器的初始化列表构造函数 + +```c++ unordered_node_map(std::initializer_list il, size_type n, const hasher& hf, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`a` 作为分配器,使用默认的键相等谓词,并将 `il` 中的元素插入其中。 + +[horizontal] +要求;; `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +=== 析构函数 + +```c++ ~unordered_node_map(); ``` + +[horizontal] +注意:析构函数会应用于每个元素,并且所有内存都会被释放。 + +--- + +=== 赋值操作 + +==== 拷贝赋值 + +```c++ unordered_node_map& operator=(unordered_node_map const& other); ``` + +赋值运算符。销毁之前存在的元素,从 `other` 拷贝赋值哈希函数和谓词,如果 `Alloc::propagate_on_container_copy_assignment` 存在且 `Alloc::propagate_on_container_copy_assignment::value` 为 `true`,则从 `other` 拷贝赋值分配器,最后插入 `other` 中元素的副本。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 + +--- + +==== 移动赋值 +```c++ +unordered_node_map& operator=(unordered_node_map&& other) noexcept((boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_move_assignment::value) && std::is_same::value); +``` +移动赋值运算符。销毁之前存在的元素,交换来自 `other` 的哈希函数和谓词,并且如果 `Alloc::propagate_on_container_move_assignment` 存在且 `Alloc::propagate_on_container_move_assignment::value` 为 `true`,则从 `other` 移动赋值分配器。如果此时分配器与 `other.get_allocator()` 相等,则 `other` 的内部桶数组直接转移给当前容器;否则,插入从 `other` 元素移动构造而来的副本。如果启用了统计信息(详见 xref:unordered_node_map_boost_unordered_enable_stats[`BOOST_UNORDERED_ENABLE_STATS`]),则当最终分配器与 `other.get_allocator()` 相等时,从 `other` 转移内部的统计信息,并且始终会调用 `other.reset_stats()`。 + +--- + +==== 初始化列表赋值 +```c++ unordered_node_map& operator=(std::initializer_list il); ``` + +从初始化列表中的值进行赋值。所有之前存在的元素都会被销毁。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 + +=== 迭代器 + +==== begin +```c++ iterator begin() noexcept; const_iterator begin() const noexcept; ``` + +[horizontal] +返回:指向容器第一个元素的迭代器,如果容器为空则返回容器的尾后迭代器。 +复杂度:O(`bucket_count()`) + +--- + +==== end +```c++ iterator end() noexcept; const_iterator end() const noexcept; ``` + +[horizontal] +返回:指向容器尾后值的迭代器。 + +--- + +==== cbegin +```c++ const_iterator cbegin() const noexcept; ``` + +[horizontal] +返回:指向容器第一个元素的 `const_iterator`,如果容器为空则返回容器的尾后值。 +复杂度:O(`bucket_count()`) + +--- + +==== cend +```c++ const_iterator cend() const noexcept; ``` + +[horizontal] +返回:指向容器尾后值的 `const_iterator`。 + +--- + +=== 大小与容量 + +==== 空 + +```c++ [[nodiscard]] bool empty() const noexcept; ``` + +[horizontal] +Returns:;; `size() == 0` + +--- + +==== 大小 + +```c++ size_type size() const noexcept; ``` + +[horizontal] +Returns:;; `std::distance(begin(), end())` + +--- + +==== max_size + +```c++ size_type max_size() const noexcept; ``` + +[horizontal] +返回:可能的最大容器的 `size()`。 + +--- + +=== 修改器 + +==== 原地构造 +```c++ template std::pair emplace(Args&&... args); ``` + +当且仅当容器中没有具有等价键的元素时,才插入一个使用参数 `args` 构造的对象。 + +[horizontal] +要求:`value_type` 可从 `args` 构造。 +返回:返回类型的 `bool` 分量为 `true` 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +如果 `args...` 的形式为 `k,v`,则该函数会延迟构造整个对象,直到确定应该插入元素为止,仅使用 `k` 参数进行检查。当 `key_type` 可移动构造或 `k` 参数本身就是 `key_type` 类型时,此优化生效。 + +--- + +==== emplace_hint +```c++ template iterator emplace_hint(const_iterator position, Args&&... args); ``` + +当且仅当容器中没有具有等价键的元素时,才插入一个使用参数 `args` 构造的对象。 + +`position` 是关于元素插入位置的建议。此实现会忽略该建议。 + +[horizontal] +要求:`value_type` 可从 `args` 构造。 +返回:返回类型的 `bool` 分量为 `true` 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +如果 `args...` 的形式为 `k,v`,则该函数会延迟构造整个对象,直到确定应该插入元素为止,仅使用 `k` 参数进行检查。当 `key_type` 可移动构造或 `k` 参数本身就是 `key_type` 类型时,此优化生效。 + +--- + +==== 复制插入 +```c++ std::pair insert(const value_type& obj); std::pair insert(const init_type& obj); ``` + +当且仅当容器中没有具有等价键的元素时,才将 `obj` 插入容器。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 +返回:返回类型的 `bool` 分量为 `true` 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +形式为 `insert(x)` 的调用,如果 `x` 可以同等转换为 `const value_type&` 和 `const init_type&`,则不会产生歧义,并且会选择 `init_type` 重载。 + +--- + +==== 移动插入 +```c++ std::pair insert(value_type&& obj); std::pair insert(init_type&& obj); ``` + +当且仅当容器中没有具有等价键的元素时,才将 `obj` 插入容器。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^]。 +返回:返回类型的 `bool` 分量为 `true` 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +形式为 `insert(x)` 的调用,如果 `x` 可以同等转换为 `value_type&&` 和 `init_type&&`,则不会产生歧义,并且会选择 `init_type` 重载。 + +--- + +==== 带提示的复制插入 +```c++ +iterator insert(const_iterator hint, const value_type& obj); +iterator insert(const_iterator hint, const init_type& obj); +``` +当且仅当容器中没有具有等价键的元素时,才将 `obj` 插入容器。 + +`hint` 是关于元素插入位置的建议。此实现会忽略该建议。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 +返回:返回类型的 `bool` 分量为 `true` 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +形式为 `insert(hint, x)` 的调用,如果 `x` 可以同等转换为 `const value_type&` 和 `const init_type&`,则不会产生歧义,并且会选择 `init_type` 重载。 + +--- + +==== 带提示的移动插入 +```c++ iterator insert(const_iterator hint, value_type&& obj); iterator insert(const_iterator hint, init_type&& obj); ``` + +当且仅当容器中没有具有等价键的元素时,才将 `obj` 插入容器。 + +`hint` 是关于元素插入位置的建议。此实现会忽略该建议。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^]。 +返回:返回类型的 `bool` 分量为 `true` 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +形式为 `insert(hint, x)` 的调用,如果 `x` 可以同等转换为 `value_type&&` 和 `init_type&&`,则不会产生歧义,并且会选择 `init_type` 重载。 + +--- + +==== 迭代器范围插入 +```c++ template void insert(InputIterator first, InputIterator last); ``` + +将一个范围内的元素插入容器中。当且仅当容器中没有具有等价键的元素时,才会插入这些元素。 + +[horizontal] +要求:`value_type` 可以从 `*first` 出发在容器中进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^]。 +抛出:当插入单个元素时,如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== 初始化列表插入 +```c++ void insert(std::initializer_list); ``` + +将一个范围内的元素插入容器中。当且仅当容器中没有具有等价键的元素时,才会插入这些元素。 + +[horizontal] +要求:`value_type` 可以在容器中进行 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 +抛出:当插入单个元素时,如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== 节点插入 +```c++ insert_return_type insert(node_type&& nh); ``` + +如果 `nh` 非空,则当且仅当容器中没有键与 `nh.key()` 等价的元素时,才将关联的元素插入容器。函数返回时 `nh` 变为空。 + +[horizontal] +返回:一个 `insert_return_type` 对象,由 `position`、`inserted` 和 `node` 构造而成。 +* 若 `nh` 为空,则 `inserted` 为 `false` , `position` 为 `end()` ,且 `node` 为空。 +* 否则,若插入操作发生,则 `inserted` 为 `true` , `position` 指向被插入的元素,且 `node` 为空。 +* 若插入操作失败,则 `inserted` 为 `false` , `node` 保留 `nh` 的原始值,且 `position` 指向与 `nh.key()` 等效的元素。 +若 `nh` 非空,则当且仅当容器中不存在键等价于 `nh.key()` 的元素时,插入其关联元素。函数返回时 `nh` 为空。 + +--- + +==== 带提示的节点插入 +```c++ iterator insert(const_iterator hint, node_type&& nh); ``` + +如果 `nh` 非空,则当且仅当容器中没有键与 `nh.key()` 等价的元素时,才将关联的元素插入容器。如果插入发生,则 `nh` 变为空;否则 `nh` 不变。 + +`hint` 是关于元素插入位置的建议。此实现会忽略该建议。 + +[horizontal] +返回:如果 `nh` 为空,则返回的迭代器为 `end()`。如果插入发生,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:如果 `nh` 非空且 `nh` 与容器的分配器不相等,则行为未定义。 + +--- + +==== try_emplace +```c++ +template +std::pair try_emplace(const key_type& k, Args&&... args); +template +std::pair try_emplace(key_type&& k, Args&&... args); +template +std::pair try_emplace(K&& k, Args&&... args); +``` + +如果容器中不存在键为 `k` 的元素,则插入一个新元素。 + +如果已存在键为 `k` 的元素,则该函数不执行任何操作。 + +[horizontal] +返回:返回类型的 `bool` 分量为 `true` 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:此函数类似于 xref:#unordered_node_map_emplace[emplace],区别在于如果存在具有等价键的元素,则不构造 `value_type`;否则,构造形式为: + +```c++ +// first two overloads +```c++ +value_type(std::piecewise_construct, + std::forward_as_tuple(std::forward(k)), + std::forward_as_tuple(std::forward(args)...)) +``` + +// third overload +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) ``` + +与 xref:#unordered_node_map_emplace[emplace] 不同,后者只是将所有参数直接转发给 `value_type` 的构造函数。 + +可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 都不能从 `K` 隐式转换时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +-- + +--- + +==== 带提示的 try_emplace +```c++ template iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); template iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); template iterator try_emplace(const_iterator hint, K&& k, Args&&... args); ``` + +如果容器中不存在键为 `k` 的元素,则插入一个新元素。 + +如果已存在键为 `k` 的元素,则该函数不执行任何操作。 + +`hint` 是关于元素插入位置的建议。此实现会忽略该建议。 + +[horizontal] +返回:如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:此函数类似于 xref:#unordered_node_map_emplace_hint[emplace_hint],区别在于如果存在具有等价键的元素,则不构造 `value_type`;否则,构造形式为: + +```c++ +// first two overloads +```c++ +value_type(std::piecewise_construct, + std::forward_as_tuple(std::forward(k)), + std::forward_as_tuple(std::forward(args)...)) +``` + +// third overload +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(args)...)) ``` + +与 xref:#unordered_node_map_emplace_hint[emplace_hint] 不同,后者只是将所有参数直接转发给 `value_type` 的构造函数。 + +可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 都不能从 `K` 隐式转换时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +-- + +--- + +==== insert_or_assign +```c++ +template +std::pair insert_or_assign(const key_type& k, M&& obj); +template +std::pair insert_or_assign(key_type&& k, M&& obj); +template +std::pair insert_or_assign(K&& k, M&& obj); +``` + +向容器中插入一个新元素,或通过赋值更新现有元素的值。 + +如果存在键为 `k` 的元素,则通过赋值 `std::forward(obj)` 来更新它。 + +如果不存在这样的元素,则将其添加到容器中,形式为: +```c++ +// first two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(obj))) + +// third overload +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(obj))) ``` + +[horizontal] +返回:返回类型的 `bool` 分量为 `true` 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +`template` 仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 带提示位置的 insert_or_assign +```c++ template iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); template iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); template iterator insert_or_assign(const_iterator hint, K&& k, M&& obj); ``` + +向容器中插入一个新元素,或通过赋值更新现有元素的值。 + +如果存在键为 `k` 的元素,则通过赋值 `std::forward(obj)` 来更新它。 + +如果不存在这样的元素,则将其添加到容器中,形式为: +```c++ +// first two overloads +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(obj))) + +// third overload +value_type(std::piecewise_construct, std::forward_as_tuple(std::forward(k)), std::forward_as_tuple(std::forward(obj))) ``` + +`hint` 是关于元素插入位置的建议。此实现会忽略该建议。 + +[horizontal] +返回:如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +`template` 仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + + +==== 按位置擦除 + +[source,c++,subs=+quotes] +---- +_convertible-to-iterator_ erase(iterator position); +_convertible-to-iterator_ erase(const_iterator position); +---- + +擦除由 `position` 指向的元素。 + +[horizontal] +返回:一个不透明对象,可隐式转换为 `position` 在被擦除之前紧后位置的 `iterator` 或 `const_iterator`。 +抛出:不抛出任何异常。 +注意:返回的不透明对象只能被丢弃或立即转换为 `iterator` 或 `const_iterator`。 + +--- + +==== 通过键擦除 +```c++ size_type erase(const key_type& k); template size_type erase(K&& k); ``` + +擦除所有键与 `k` 等价的元素。 + +[horizontal] +返回:被擦除的元素数量。 +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 都不能从 `K` 隐式转换时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 范围擦除 + +```c++ iterator erase(const_iterator first, const_iterator last); ``` + +Erases the elements in the range from `first` to `last`. + +[horizontal] +返回:被擦除元素之后的迭代器——即 `last`。 +抛出:在此实现中不抛出任何异常(既不调用 `hasher` 也不调用 `key_equal` 对象)。 + +--- + +==== 交换 +```c++ void swap(unordered_node_map& other) noexcept(boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_swap::value); ``` + +交换容器的内容与参数的内容。 + +如果 `Allocator::propagate_on_container_swap` 被声明且 `Allocator::propagate_on_container_swap::value` 为 `true`,则交换容器的分配器。否则,使用不相等的分配器进行交换将导致未定义行为。 + +[horizontal] +抛出:除非 `key_equal` 或 `hasher` 在交换时抛出异常,否则不抛出任何异常。 + +--- + +==== 通过位置提取 +```c++ node_type extract(const_iterator position); ``` + +提取 `position` 所指向的元素。 + +[horizontal] +返回:一个持有被提取元素的 `node_type` 对象。 +抛出:不抛出任何异常。 + +--- + +==== 通过键提取 +```c++ node_type extract(const key_type& k); template node_type extract(K&& k); ``` + +提取键与 `k` 等价的元素(如果存在)。 + +[horizontal] +返回:一个持有被提取元素的 `node_type` 对象;如果未提取到任何元素,则返回空。 +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== pull +```c++ init_type pull(const_iterator position); ``` + +从 `position` 指向的元素移动构造一个 `init_value` 对象 `x`,擦除该元素并返回 `x`。 + +--- + +==== 清空 +```c++ void clear() noexcept; ``` + +擦除容器中的所有元素。 + +[horizontal] +后置条件:`size() == 0`,`max_load() >= max_load_factor() * bucket_count()` + +--- + +==== 合并 +```c++ template void merge(unordered_node_map& source); template void merge(unordered_node_map&& source); ``` + +从 `source` 转移所有键在 `*this` 中尚未存在的元素节点。 + +[horizontal] +要求:`this->get_allocator() == source.get_allocator()`。 +注意:会使被转移元素的迭代器失效。如果 `*this` 的结果大小大于其原始最大负载,则会使与 `*this` 关联的所有迭代器失效。 + +--- + +=== 观察器 + +==== get_allocator +``` allocator_type get_allocator() const noexcept; ``` + +[horizontal] +返回:容器的分配器。 + +--- + +==== 哈希函数 +``` hasher hash_function() const; ``` + +[horizontal] +返回:容器的哈希函数。 + +--- + +==== key_eq +``` key_equal key_eq() const; ``` + +[horizontal] +返回:容器的键相等谓词。 + +--- + +=== 查找 + +==== find +```c++ iterator find(const key_type& k); const_iterator find(const key_type& k) const; template iterator find(const K& k); + +``` + +[horizontal] +返回:指向键与 `k` 等价的元素的迭代器;如果不存在这样的元素,则返回 `end()`。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== count +```c++ size_type count(const key_type& k) const; template size_type count(const K& k) const; ``` + +[horizontal] +返回:键与 `k` 等价的元素数量。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 包含 +```c++ bool contains(const key_type& k) const; template bool contains(const K& k) const; ``` + +[horizontal] +返回:一个布尔值,指示容器中是否存在键等于 `key` 的元素。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== equal_range +```c++ std::pair equal_range(const key_type& k); std::pair equal_range(const key_type& k) const; template std::pair equal_range(const K& k); template std::pair equal_range(const K& k) const; ``` + +[horizontal] +返回:包含所有键与 `k` 等价的元素的范围。如果容器不包含任何此类元素,则返回 `std::make_pair(b.end(), b.end())`。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== operator++[++++]++ +```c++ mapped_type& operator[](const key_type& k); mapped_type& operator[](key_type&& k); template mapped_type& operator[](K&& k); ``` + +[horizontal] +效果:如果容器中不存在键与 `k` 等价的元素,则插入值 `std::pair(k, mapped_type())`。 +返回:对 `x.second` 的引用,其中 `x` 是容器中已存在的元素,或是新插入的键与 `k` 等价的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== at +```c++ mapped_type& at(const key_type& k); const mapped_type& at(const key_type& k) const; template mapped_type& at(const K& k); template const mapped_type& at(const K& k) const; ``` + +[horizontal] +返回:对 `x.second` 的引用,其中 `x` 是键与 `k` 等价的(唯一)元素。 +抛出:如果不存在这样的元素,则抛出 `std::out_of_range` 类型的异常对象。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +=== 桶接口 + +==== bucket_count +```c++ size_type bucket_count() const noexcept; ``` + +[horizontal] +返回:桶数组的大小。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ float load_factor() const noexcept; ``` + +[horizontal] +返回:`static_cast(size())/static_cast(bucket_count())`,如果 `bucket_count() == 0` 则返回 `0`。 + +--- + +==== max_load_factor + +```c++ float max_load_factor() const noexcept; ``` + +[horizontal] +返回:容器的最大负载因子。 + +--- + +==== 设置最大负载因子 +```c++ void max_load_factor(float z); ``` + +[horizontal] +效果:不执行任何操作,因为用户不允许更改此参数。保留此函数是为了与 `boost::unordered_map` 保持兼容。 + +--- + + +==== max_load + +```c++ size_type max_load() const noexcept; ``` + +[horizontal] +返回:容器在不触发重哈希的情况下能够容纳的最大元素数量(假设不再有元素被擦除)。 +注意:在构造、重哈希或清空操作之后,容器的最大负载至少为 `max_load_factor() * bucket_count()`。在高负载条件下,该数值可能会因元素擦除而减小。 + +--- + +==== 重哈希 +```c++ void rehash(size_type n); ``` + +必要时更改桶数组的大小,使其至少包含 `n` 个桶,并且使得负载因子小于或等于最大负载因子。在适用的情况下,这将增大或缩小与容器关联的 `bucket_count()`。 + +当 `size() == 0` 时,`rehash(0)` 将释放底层的桶数组。如果提供的分配器使用异形指针,则会随后执行一次默认分配。 + +使迭代器失效并改变元素的顺序。 + +[horizontal] +抛出:如果抛出异常(除非是由容器的哈希函数或比较函数抛出的),则该函数无效果。 + +--- + +==== 保留 +```c++ void reserve(size_type n); ``` + +等价于 `a.rehash(ceil(n / a.max_load_factor()))`。 + +与 `rehash` 类似,此函数可用于增大或缩小容器中的桶数量。 + +使迭代器失效并改变元素的顺序。 + +[horizontal] +抛出:如果抛出异常(除非是由容器的哈希函数或比较函数抛出的),则该函数无效果。 + +--- + +=== 统计信息 + +==== get_stats +```c++ stats get_stats() const; ``` + +[horizontal] +返回:容器迄今为止所执行插入和查找操作的统计描述。 +注意:仅在 xref:reference/stats.adoc#stats[统计计算] 通过 xref:unordered_node_map_boost_unordered_enable_stats[`BOOST_UNORDERED_ENABLE_STATS`] 启用时才可用。 + +--- + +==== reset_stats +```c++ void reset_stats() noexcept; ``` + +[horizontal] +效果:将容器内部维护的统计信息归零。 +注意:仅在 xref:reference/stats.adoc#stats[统计计算] 通过 xref:unordered_node_map_boost_unordered_enable_stats[`BOOST_UNORDERED_ENABLE_STATS`] 启用时才可用。 + +--- + +=== 推导指引 +如果以下任何一条件为真,则推导指引将不参与重载决议: + +- 它具有一个 `InputIterator` 模板参数,并且为该参数推导出的类型不符合输入迭代器的要求。 +- 它具有一个 `Allocator` 模板参数,并且为该参数推导出的类型不符合分配器的要求。 +- 它具有一个 `Hash` 模板参数,并且为该参数推导出的类型是整数类型或符合分配器的要求。 +- 它具有一个 `Pred` 模板参数,并且为该参数推导出的类型符合分配器的要求。 + +推导指引中的 `size_type` 参数类型指的是由该推导指引所推导出的容器类型的 `size_type` 成员类型。其默认值与所选构造函数的默认值一致。 + +==== _iter-value-type_ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-value-type__ = + typename std::iterator_traits::value_type; // exposition only +----- + +==== __iter-key-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-key-type__ = std::remove_const_t< + std::tuple_element_t<0, xref:#unordered_node_map_iter_value_type[__iter-value-type__]>>; // exposition only +----- + +==== __iter-mapped-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-mapped-type__ = + std::tuple_element_t<1, xref:#unordered_node_map_iter_value_type[__iter-value-type__]>; // exposition only +----- + +==== __iter-to-alloc-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-to-alloc-type__ = std::pair< + std::add_const_t>>, + std::tuple_element_t<1, xref:#unordered_node_map_iter_value_type[__iter-value-type__]>>; // exposition only +----- + +=== 相等性比较 + +==== operator +```c++ template bool operator==(const unordered_node_map& x, const unordered_node_map& y); ``` + +若 `x.size() == y.size()` 且对于 `x` 中的每个元素,在 `y` 中均存在一个具有相同键且值相等(使用 `operator==` 比较值类型)的元素,则返回 `true`。 + +[horizontal] +注意:如果两个容器不具有等价的相等谓词,则行为未定义。 + +--- + +==== operator! +```c++ template bool operator!=(const unordered_node_map& x, const unordered_node_map& y); ``` + +若 `x.size() == y.size()` 且对于 `x` 中的每个元素,在 `y` 中均存在一个具有相同键且值相等(使用 `operator==` 比较值类型)的元素,则返回 `false`。 + +[horizontal] +注意:如果两个容器不具有等价的相等谓词,则行为未定义。 + +=== 交换 +```c++ template void swap(unordered_node_map& x, unordered_node_map& y) noexcept(noexcept(x.swap(y))); ``` + +交换 `x` 和 `y` 的内容。 + +如果 `Allocator::propagate_on_container_swap` 被声明且 `Allocator::propagate_on_container_swap::value` 为 `true`,则交换容器的分配器。否则,使用不相等的分配器进行交换将导致未定义行为。 + +[horizontal] +效果:`x.swap(y)` +抛出:除非 `key_equal` 或 `hasher` 在交换时抛出异常,否则不抛出任何异常。 + +--- + +=== erase_if +```c++ template typename unordered_node_map::size_type erase_if(unordered_node_map& c, Predicate pred); ``` + +遍历容器 `c`,并移除所有使给定谓词返回 `true` 的元素。 + +[horizontal] +返回:被擦除的元素数量。 +注意:等价于: +```c++ +auto original_size = c.size(); +for (auto i = c.begin(), last = c.end(); i != last; ) { + if (pred(*i)) { + i = c.erase(i); + } else { + ++i; + } +} +return original_size - c.size(); +``` +注意,传递给 `pred` 的引用是非常量的。 + +=== 序列化 + +`unordered_node_map` 可以通过本库提供的 API,借助 link:../../../../../serialization/index.html[Boost.Serialization^] 进行归档/恢复。支持常规归档和 XML 归档。 + +==== 将 unordered_node_map 保存到归档中 + +将 `unordered_node_map` `x` 的所有元素保存到归档(XML 归档) `ar` 。 + +[horizontal] +要求:`std::remove_const::type` 和 `std::remove_const::type` 必须是可序列化的(对于 XML 归档需支持 XML 序列化),并且它们必须支持 Boost.Serialization 的 `save_construct_data`/`load_construct_data` 协议(该协议由满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 的类型自动支持)。 + +--- + +==== 从归档中加载 unordered_node_map + +删除 `unordered_node_map` 容器 `x` 中所有已存在的元素,并从归档(XML 归档)`ar` 中插入从原始 `unordered_node_map` 容器 `other` 保存到 `ar` 所读取存储中的元素恢复出的副本。 + +[horizontal] +要求:`key_type` 和 `mapped_type` 必须分别能够从 `std::remove_const::type&&` 和 `std::remove_const::type&&` 构造。`x.key_equal()` 在功能上必须等价于 `other.key_equal()`。 + +--- + +==== 将迭代器/常量迭代器保存到归档 + +将迭代器(或常量迭代器)`it` 的位置信息保存到归档(XML 归档)`ar` 中。`it` 可以是一个 `end()` 迭代器。 + +[horizontal] +要求:`it` 所指向的 `unordered_node_map` 容器 `x` 必须先被保存到 `ar` 中,并且在保存 `x` 和保存 `it` 之间,不能对 `x` 执行任何修改操作。 + +--- + +==== 从归档加载迭代器/常量迭代器 + +使迭代器(或常量迭代器)`it` 指向原始迭代器(或常量迭代器)被保存到归档(XML 归档)`ar` 所读取存储中的位置恢复后的位置。 + +[horizontal] +要求:如果 `x` 是 `it` 所指向的 `unordered_node_map` 容器,则在加载 `x` 和加载 `it` 之间,不能对 `x` 执行任何修改操作。 diff --git a/doc/modules/ROOT/pages/reference/unordered_node_set_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/unordered_node_set_zh_Hans.adoc new file mode 100644 index 0000000..e80a0e6 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/unordered_node_set_zh_Hans.adoc @@ -0,0 +1,1187 @@ +[#unordered_node_set] +== 类模板 unordered_node_set + +:idprefix: unordered_node_set_ + +`boost::unordered_node_set` — 一种基于节点的开放寻址无序关联容器,用于存储唯一值。 + +`boost::unordered_node_set` 使用与 `boost::unordered_flat_set` 类似的开放寻址布局,但由于其基于节点的特性,它提供了指针稳定性和节点处理功能。其性能介于 `boost::unordered_set` 和 `boost::unordered_flat_set` 之间。 + +由于使用开放寻址,`boost::unordered_node_set` 的接口在多个方面与 `boost::unordered_set`/`std::unordered_set` 有所不同: + +- `begin()` 不是常数时间复杂度的。 +- 没有用于桶操作的 API(除了 `bucket_count` 之外)。 +- 容器的最大负载因子由内部管理,用户无法设置。 + +除此之外, `boost::unordered_node_set` 几乎可以完全替代标准无序关联容器。 + +=== 概要 + +[listing,subs="+macros,+quotes"] +----- +// #include xref:reference/header_unordered_node_set.adoc[``] + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator> + class unordered_node_set { + public: + // types + using key_type = Key; + using value_type = Key; + using init_type = Key; + using hasher = Hash; + using key_equal = Pred; + using allocator_type = Allocator; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + using iterator = _implementation-defined_; + using const_iterator = _implementation-defined_; + + using node_type = _implementation-defined_; + using insert_return_type = _implementation-defined_; + + using stats = xref:reference/stats.adoc#stats_stats_type[__stats-type__]; // if statistics are xref:unordered_node_set_boost_unordered_enable_stats[enabled] + + // construct/copy/destroy + xref:#unordered_node_set_default_constructor[unordered_node_set](); + explicit xref:#unordered_node_set_bucket_count_constructor[unordered_node_set](size_type n, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + template + xref:#unordered_node_set_iterator_range_constructor[unordered_node_set](InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#unordered_node_set_copy_constructor[unordered_node_set](const unordered_node_set& other); + xref:#unordered_node_set_move_constructor[unordered_node_set](unordered_node_set&& other); + template + xref:#unordered_node_set_iterator_range_constructor_with_allocator[unordered_node_set](InputIterator f, InputIterator l, const allocator_type& a); + explicit xref:#unordered_node_set_allocator_constructor[unordered_node_set](const Allocator& a); + xref:#unordered_node_set_copy_constructor_with_allocator[unordered_node_set](const unordered_node_set& other, const Allocator& a); + xref:#unordered_node_set_move_constructor_with_allocator[unordered_node_set](unordered_node_set&& other, const Allocator& a); + xref:#unordered_node_set_move_constructor_from_concurrent_node_set[unordered_node_set](concurrent_node_set&& other); + xref:#unordered_node_set_initializer_list_constructor[unordered_node_set](std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#unordered_node_set_bucket_count_constructor_with_allocator[unordered_node_set](size_type n, const allocator_type& a); + xref:#unordered_node_set_bucket_count_constructor_with_hasher_and_allocator[unordered_node_set](size_type n, const hasher& hf, const allocator_type& a); + template + xref:#unordered_node_set_iterator_range_constructor_with_bucket_count_and_allocator[unordered_node_set](InputIterator f, InputIterator l, size_type n, const allocator_type& a); + template + xref:#unordered_node_set_iterator_range_constructor_with_bucket_count_and_hasher[unordered_node_set](InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); + xref:#unordered_node_set_initializer_list_constructor_with_allocator[unordered_node_set](std::initializer_list il, const allocator_type& a); + xref:#unordered_node_set_initializer_list_constructor_with_bucket_count_and_allocator[unordered_node_set](std::initializer_list il, size_type n, + const allocator_type& a); + xref:#unordered_node_set_initializer_list_constructor_with_bucket_count_and_hasher_and_allocator[unordered_node_set](std::initializer_list il, size_type n, const hasher& hf, + const allocator_type& a); + xref:#unordered_node_set_destructor[~unordered_node_set](); + unordered_node_set& xref:#unordered_node_set_copy_assignment[operator++=++](const unordered_node_set& other); + unordered_node_set& xref:#unordered_node_set_move_assignment[operator++=++](unordered_node_set&& other) ++noexcept( + (boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_move_assignment::value) && + std::is_same::value);++ + unordered_node_set& xref:#unordered_node_set_initializer_list_assignment[operator++=++](std::initializer_list); + allocator_type xref:#unordered_node_set_get_allocator[get_allocator]() const noexcept; + + // iterators + iterator xref:#unordered_node_set_begin[begin]() noexcept; + const_iterator xref:#unordered_node_set_begin[begin]() const noexcept; + iterator xref:#unordered_node_set_end[end]() noexcept; + const_iterator xref:#unordered_node_set_end[end]() const noexcept; + const_iterator xref:#unordered_node_set_cbegin[cbegin]() const noexcept; + const_iterator xref:#unordered_node_set_cend[cend]() const noexcept; + + // capacity + ++[[nodiscard]]++ bool xref:#unordered_node_set_empty[empty]() const noexcept; + size_type xref:#unordered_node_set_size[size]() const noexcept; + size_type xref:#unordered_node_set_max_size[max_size]() const noexcept; + + // modifiers + template std::pair xref:#unordered_node_set_emplace[emplace](Args&&... args); + template iterator xref:#unordered_node_set_emplace_hint[emplace_hint](const_iterator position, Args&&... args); + std::pair xref:#unordered_node_set_copy_insert[insert](const value_type& obj); + std::pair xref:#unordered_node_set_move_insert[insert](value_type&& obj); + template std::pair xref:#unordered_node_set_transparent_insert[insert](K&& k); + iterator xref:#unordered_node_set_copy_insert_with_hint[insert](const_iterator hint, const value_type& obj); + iterator xref:#unordered_node_set_move_insert_with_hint[insert](const_iterator hint, value_type&& obj); + template iterator xref:#unordered_node_set_transparent_insert_with_hint[insert](const_iterator hint, K&& k); + template void xref:#unordered_node_set_insert_iterator_range[insert](InputIterator first, InputIterator last); + void xref:#unordered_node_set_insert_initializer_list[insert](std::initializer_list); + insert_return_type xref:#unordered_node_set_insert_node[insert](node_type&& nh); + iterator xref:#unordered_node_set_insert_node_with_hint[insert](const_iterator hint, node_type&& nh); + + _convertible-to-iterator_ xref:#unordered_node_set_erase_by_position[erase](iterator position); + _convertible-to-iterator_ xref:#unordered_node_set_erase_by_position[erase](const_iterator position); + size_type xref:#unordered_node_set_erase_by_key[erase](const key_type& k); + template size_type xref:#unordered_node_set_erase_by_key[erase](K&& k); + iterator xref:#unordered_node_set_erase_range[erase](const_iterator first, const_iterator last); + void xref:#unordered_node_set_swap[swap](unordered_node_set& other) + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_swap::value); + node_type xref:#unordered_node_set_extract_by_position[extract](const_iterator position); + node_type xref:#unordered_node_set_extract_by_key[extract](const key_type& key); + template node_type xref:#unordered_node_set_extract_by_key[extract](K&& key); + init_type xref:#unordered_node_set_pull[pull](const_iterator position); + void xref:#unordered_node_set_clear[clear]() noexcept; + + template + void xref:#unordered_node_set_merge[merge](unordered_node_set& source); + template + void xref:#unordered_node_set_merge[merge](unordered_node_set&& source); + + // observers + hasher xref:#unordered_node_set_hash_function[hash_function]() const; + key_equal xref:#unordered_node_set_key_eq[key_eq]() const; + + // set operations + iterator xref:#unordered_node_set_find[find](const key_type& k); + const_iterator xref:#unordered_node_set_find[find](const key_type& k) const; + template + iterator xref:#unordered_node_set_find[find](const K& k); + template + const_iterator xref:#unordered_node_set_find[find](const K& k) const; + size_type xref:#unordered_node_set_count[count](const key_type& k) const; + template + size_type xref:#unordered_node_set_count[count](const K& k) const; + bool xref:#unordered_node_set_contains[contains](const key_type& k) const; + template + bool xref:#unordered_node_set_contains[contains](const K& k) const; + std::pair xref:#unordered_node_set_equal_range[equal_range](const key_type& k); + std::pair xref:#unordered_node_set_equal_range[equal_range](const key_type& k) const; + template + std::pair xref:#unordered_node_set_equal_range[equal_range](const K& k); + template + std::pair xref:#unordered_node_set_equal_range[equal_range](const K& k) const; + + // bucket interface + size_type xref:#unordered_node_set_bucket_count[bucket_count]() const noexcept; + + // hash policy + float xref:#unordered_node_set_load_factor[load_factor]() const noexcept; + float xref:#unordered_node_set_max_load_factor[max_load_factor]() const noexcept; + void xref:#unordered_node_set_set_max_load_factor[max_load_factor](float z); + size_type xref:#unordered_node_set_max_load[max_load]() const noexcept; + void xref:#unordered_node_set_rehash[rehash](size_type n); + void xref:#unordered_node_set_reserve[reserve](size_type n); + + // statistics (if xref:unordered_node_set_boost_unordered_enable_stats[enabled]) + stats xref:#unordered_node_set_get_stats[get_stats]() const; + void xref:#unordered_node_set_reset_stats[reset_stats]() noexcept; + }; + + // Deduction Guides + template>, + class Pred = std::equal_to>, + class Allocator = std::allocator>> + unordered_node_set(InputIterator, InputIterator, typename xref:#unordered_node_set_deduction_guides[__see below__]::size_type = xref:#unordered_node_set_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_node_set, Hash, Pred, Allocator>; + + template, class Pred = std::equal_to, + class Allocator = std::allocator> + unordered_node_set(std::initializer_list, typename xref:#unordered_node_set_deduction_guides[__see below__]::size_type = xref:#unordered_node_set_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_node_set; + + template + unordered_node_set(InputIterator, InputIterator, typename xref:#unordered_node_set_deduction_guides[__see below__]::size_type, Allocator) + -> unordered_node_set, + boost::hash>, + std::equal_to>, Allocator>; + + template + unordered_node_set(InputIterator, InputIterator, Allocator) + -> unordered_node_set, + boost::hash>, + std::equal_to>, Allocator>; + + template + unordered_node_set(InputIterator, InputIterator, typename xref:#unordered_node_set_deduction_guides[__see below__]::size_type, Hash, + Allocator) + -> unordered_node_set, Hash, + std::equal_to>, Allocator>; + + template + unordered_node_set(std::initializer_list, typename xref:#unordered_node_set_deduction_guides[__see below__]::size_type, Allocator) + -> unordered_node_set, std::equal_to, Allocator>; + + template + unordered_node_set(std::initializer_list, Allocator) + -> unordered_node_set, std::equal_to, Allocator>; + + template + unordered_node_set(std::initializer_list, typename xref:#unordered_node_set_deduction_guides[__see below__]::size_type, Hash, Allocator) + -> unordered_node_set, Allocator>; + +} // namespace unordered +} // namespace boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_Key_ +|`Key` must be https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the container. + +|_Hash_ +|A unary function object type that acts a hash function for a `Key`. It takes a single argument of type `Key` and returns a value of type `std::size_t`. + +|_Pred_ +|A binary function object that induces an equivalence relation on values of type `Key`. It takes two arguments of type `Key` and returns a value of type `bool`. + +|_Allocator_ +|An allocator whose value type is the same as the container's value type. +支持使用 https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[异形指针] 的分配器。 + +|=== + +容器的元素节点被保存在内部的 _桶数组_ 中。节点根据其元素的哈希码被插入到对应的桶中,但如果该桶已被占用(发生 _冲突_),则会使用原始位置附近的一个可用桶。 + +桶数组的大小可以通过调用 `insert`/`emplace` 自动增加,或者作为调用 `rehash`/`reserve` 的结果。容器的 _负载因子_(元素数量除以桶数量)永远不会大于 `max_load_factor()`,但在容器规模较小时,实现可能会允许更高的负载。 + +如果 `link:../../../../../container_hash/doc/html/hash.html#ref_hash_is_avalanching[hash_is_avalanching]::value` 为 `true`,则哈希函数将按原样使用;否则,会增加一个位混合后处理阶段,以牺牲额外的计算成本为代价来提高哈希质量。 + +--- + +=== 配置宏 + +==== `BOOST_UNORDERED_ENABLE_STATS` + +全局定义此宏,以启用容器的 xref:reference/stats.adoc#stats[统计计算] 功能。请注意,此选项会降低许多操作的总体性能。 + +--- + +=== 类型定义 + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ iterator; +---- + +一个常量迭代器,其值类型为 `value_type` 。 + +迭代器类别至少为前向迭代器。 + +可转换为 `const_iterator` 。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ const_iterator; +---- + +一个常量迭代器,其值类型为 `value_type` 。 + +迭代器类别至少为前向迭代器。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ node_type; +---- + +用于保存已提取容器元素的类,其建模自 https://en.cppreference.com/w/cpp/container/node_handle[NodeHandle] 。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ insert_return_type; +---- + +内部类模板的特化: + +[source,c++,subs=+quotes] +---- +template +struct _insert_return_type_ // name is exposition only +{ + Iterator position; + bool inserted; + NodeType node; +}; +---- + +其中 `Iterator` = `iterator` ,且 `NodeType` = `node_type` 。 + +--- + +=== 构造函数 + +==== 默认构造函数 +```c++ unordered_node_set(); ``` + +使用 `hasher()` 作为哈希函数、`key_equal()` 作为键相等谓词以及 `allocator_type()` 作为分配器,构造一个空容器。 + +[horizontal] +后置条件:`size() == 0` +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 桶数构造函数 +```c++ explicit unordered_node_set(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器。 + +[horizontal] +后置条件:`size() == 0` +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_node_set(InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `eql` 作为键相等性谓词、 `a` 作为分配器,并将区间 `++[++f, l)` 中的元素插入其中。 + +[horizontal] +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 复制构造函数 +```c++ unordered_node_set(unordered_node_set const& other); ``` + +拷贝构造函数。拷贝所包含的元素、哈希函数、谓词和分配器。 + +如果 `Allocator::select_on_container_copy_construction` 存在且具有正确的签名,则分配器将根据其结果进行构造。 + +[horizontal] +要求:`value_type` 可拷贝构造 + +--- + +==== 移动构造函数 +```c++ unordered_node_set(unordered_node_set&& other); ``` + +移动构造函数。`other` 的内部桶数组会被直接转移给新容器。哈希函数、谓词和分配器通过移动构造从 `other` 获得。如果启用了统计信息(详见 xref:unordered_node_set_boost_unordered_enable_stats[`BOOST_UNORDERED_ENABLE_STATS`]),则会从 `other` 转移内部的统计信息,并调用 `other.reset_stats()`。 + +--- + +==== 带分配器的迭代器范围构造函数 +```c++ template unordered_node_set(InputIterator f, InputIterator l, const allocator_type& a); ``` + +使用 `a` 作为分配器,以默认哈希函数和键相等谓词构造一个空容器,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:`hasher`、`key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 分配器构造函数 +```c++ explicit unordered_node_set(Allocator const& a); ``` + +使用分配器 `a` 构造一个空容器。 + +--- + +==== 带分配器的复制构造函数 +```c++ unordered_node_set(unordered_node_set const& other, Allocator const& a); ``` + +构造一个容器,拷贝 `other` 所包含的元素、哈希函数和谓词,但使用分配器 `a`。 + +--- + +==== 带分配器的移动构造函数 +```c++ unordered_node_set(unordered_node_set&& other, Allocator const& a); ``` + +如果 `a == other.get_allocator()`,则 `other` 的元素节点会直接转移给新容器;否则,元素将从 `other` 中的元素通过移动构造获得。哈希函数和谓词通过移动构造从 `other` 获得,而分配器则通过拷贝构造从 `a` 获得。如果启用了统计信息(详见 xref:unordered_node_set_boost_unordered_enable_stats[`BOOST_UNORDERED_ENABLE_STATS`]),则当 `a == other.get_allocator()` 时,会从 `other` 转移内部的统计信息,并且始终会调用 `other.reset_stats()`。 + +--- + +==== 从 concurrent_node_set 的移动构造函数 + +```c++ unordered_node_set(concurrent_node_set&& other); ``` + +从 xref:#concurrent_node_set[`concurrent_node_set`] 进行移动构造。`other` 的内部桶数组直接转移给新容器。哈希函数、谓词和分配器通过移动构造从 `other` 获得。如果启用了统计信息(详见 xref:unordered_node_set_boost_unordered_enable_stats[`BOOST_UNORDERED_ENABLE_STATS`]),则从 `other` 转移内部的统计信息,并调用 `other.reset_stats()`。 + +[horizontal] +复杂度:常数时间。 +并发性:在 `other` 上阻塞。 + +--- + +==== 初始化列表构造函数 +[source,c++,subs="+quotes"] +---- +unordered_node_set(std::initializer_list il, + size_type n = _implementation-defined_ + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `eql` 作为键相等性谓词、以及 `a` 作为分配器,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 必须满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带分配器的桶数构造函数 +```c++ unordered_node_set(size_type n, allocator_type const& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,使用默认的哈希函数和键相等谓词,并以 `a` 作为分配器。 + +[horizontal] +后置条件:`size() == 0` 要求:`hasher` 和 `key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带哈希函数和分配器的桶数构造函数 +```c++ unordered_node_set(size_type n, hasher const& hf, allocator_type const& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,使用默认的键相等谓词,并以 `a` 作为分配器。 + +[horizontal] +后置条件:`size() == 0` 要求:`key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数和分配器的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_node_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 作为分配器、以及默认的哈希函数和键相等性谓词,并将 `++[++f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:`hasher`、`key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数和哈希函数的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- + template + unordered_node_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `a` 作为分配器以及默认的键相等性谓词,并将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求;; `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带分配器的初始化列表构造函数 + +```c++ unordered_node_set(std::initializer_list il, const allocator_type& a); ``` + +使用 `a` 以及默认的哈希函数和键相等谓词构造一个空容器,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数和分配器的初始化列表构造函数 + +```c++ unordered_node_set(std::initializer_list il, size_type n, const allocator_type& a); ``` + +使用 `a` 以及默认的哈希函数和键相等谓词,构造一个至少包含 `n` 个桶的空容器,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]。 + +--- + +==== 带桶数、哈希函数和分配器的初始化列表构造函数 + +```c++ unordered_node_set(std::initializer_list il, size_type n, const hasher& hf, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数,`a` 作为分配器,使用默认的键相等谓词,并将 `il` 中的元素插入其中。 + +[horizontal] +要求;; `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +=== 析构函数 + +```c++ ~unordered_node_set(); ``` + +[horizontal] +注意:析构函数会应用于每个元素,并且所有内存都会被释放。 + +--- + +=== 赋值操作 + +==== 复制赋值 + +```c++ unordered_node_set& operator=(unordered_node_set const& other); ``` + +赋值运算符。销毁之前存在的元素,从 `other` 拷贝赋值哈希函数和谓词,如果 `Alloc::propagate_on_container_copy_assignment` 存在且 `Alloc::propagate_on_container_copy_assignment::value` 为 `true`,则从 `other` 拷贝赋值分配器,最后插入 `other` 中元素的副本。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^] + +--- + +==== 移动赋值 +```c++ +unordered_node_set& operator=(unordered_node_set&& other) noexcept((boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_move_assignment::value) && std::is_same::value); +``` +移动赋值运算符。销毁之前存在的元素,交换来自 `other` 的哈希函数和谓词,并且如果 `Alloc::propagate_on_container_move_assignment` 存在且 `Alloc::propagate_on_container_move_assignment::value` 为 `true`,则从 `other` 移动赋值分配器。如果此时分配器与 `other.get_allocator()` 相等,则 `other` 的内部桶数组直接转移给当前容器;否则,插入从 `other` 元素移动构造而来的副本。如果启用了统计信息(详见 xref:unordered_node_set_boost_unordered_enable_stats[`BOOST_UNORDERED_ENABLE_STATS`]),则当最终分配器与 `other.get_allocator()` 相等时,从 `other` 转移内部的统计信息,并且始终会调用 `other.reset_stats()`。 + +--- + +==== 初始化列表赋值 +```c++ unordered_node_set& operator=(std::initializer_list il); ``` + +从初始化列表中的值进行赋值。所有之前存在的元素都会被销毁。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^] + +=== 迭代器 + +==== begin +```c++ iterator begin() noexcept; const_iterator begin() const noexcept; ``` + +[horizontal] +返回:指向容器第一个元素的迭代器,如果容器为空则返回容器的尾后迭代器。 +复杂度:O(`bucket_count()`) + +--- + +==== end +```c++ iterator end() noexcept; const_iterator end() const noexcept; ``` + +[horizontal] +返回:指向容器尾后值的迭代器。 + +--- + +==== cbegin +```c++ const_iterator cbegin() const noexcept; ``` + +[horizontal] +Returns:;; A `const_iterator` referring to the first element of the container, or if the container is empty the past-the-end value for the container. Complexity:;; O(`bucket_count()`) + +--- + +==== cend +```c++ const_iterator cend() const noexcept; ``` + +[horizontal] +返回:指向容器尾后值的 `const_iterator`。 + +--- + +=== 大小与容量 + +==== 空 + +```c++ [[nodiscard]] bool empty() const noexcept; ``` + +[horizontal] +Returns:;; `size() == 0` + +--- + +==== 大小 + +```c++ size_type size() const noexcept; ``` + +[horizontal] +Returns:;; `std::distance(begin(), end())` + +--- + +==== max_size + +```c++ size_type max_size() const noexcept; ``` + +[horizontal] +返回:;; 可能的最大容器的 `size()`。 + +--- + +=== 修改器 + +==== 原地构造 +```c++ template std::pair emplace(Args&&... args); ``` + +当且仅当容器中没有具有等价键的元素时,才插入一个使用参数 `args` 构造的对象。 + +[horizontal] +要求:`value_type` 可从 `args` 构造。 +返回:返回类型的 `bool` 分量为 `true` 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== emplace_hint +```c++ template iterator emplace_hint(const_iterator position, Args&&... args); ``` + +当且仅当容器中没有具有等价键的元素时,才插入一个使用参数 `args` 构造的对象。 + +`position` 是关于元素插入位置的建议。此实现会忽略该建议。 + +[horizontal] +要求:`value_type` 可从 `args` 构造。 +返回:返回类型的 `bool` 分量为 `true` 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== 复制插入 +```c++ std::pair insert(const value_type& obj); ``` + +当且仅当容器中没有具有等价键的元素时,才将 `obj` 插入容器。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 +返回:返回类型的 `bool` 分量为 `true` 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== 移动插入 +```c++ std::pair insert(value_type&& obj); ``` + +当且仅当容器中没有具有等价键的元素时,才将 `obj` 插入容器。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^]。 +返回:返回类型的 `bool` 分量为 `true` 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== 透明插入 +```c++ template std::pair insert(K&& k); ``` + +当且仅当容器中没有具有等价键的元素时,才将在容器中插入一个从 `std::forward(k)` 构造的元素。 + +[horizontal] +要求:`value_type` 可以从 `k` 进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^]。 +返回:如果发生了插入,则返回类型的 bool 分量为 true。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +只有当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 都不能从 `K` 隐式转换时,此重载才会参与重载决议。库假定 `Hash` 可同时用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 带提示的复制插入 +```c++ iterator insert(const_iterator hint, const value_type& obj); ```当且仅当容器中没有具有等价键的元素时,才将 `obj` 插入容器。 + +`hint` 是关于元素插入位置的建议。此实现会忽略该建议。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 +返回:返回类型的 `bool` 分量为 `true` 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== 带提示的移动插入 +```c++ iterator insert(const_iterator hint, value_type&& obj); ``` + +当且仅当容器中没有具有等价键的元素时,才将 `obj` 插入容器。 + +`hint` 是关于元素插入位置的建议。此实现会忽略该建议。 + +[horizontal] +要求:`value_type` 满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^]。 +返回:返回类型的 `bool` 分量为 `true` 表示发生了插入。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== 带提示的透明插入 +```c++ template std::pair insert(const_iterator hint, K&& k); ``` + +当且仅当容器中没有具有等价键的元素时,才将在容器中插入一个从 `std::forward(k)` 构造的元素。 + +`hint` 是关于元素插入位置的建议。此实现会忽略该建议。 + +[horizontal] +要求:`value_type` 可以从 `k` 进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^]。 +返回:如果发生了插入,则返回类型的 bool 分量为 true。 +如果发生了插入,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +只有当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 都不能从 `K` 隐式转换时,此重载才会参与重载决议。库假定 `Hash` 可同时用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 迭代器范围插入 +```c++ template void insert(InputIterator first, InputIterator last); ``` + +将一个范围内的元素插入容器中。当且仅当容器中没有具有等价键的元素时,才会插入这些元素。 + +[horizontal] +要求:`value_type` 可以从 `*first` 出发在容器中进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^]。 +抛出:当插入单个元素时,如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== 初始化列表插入 +```c++ void insert(std::initializer_list); ``` + +将一个范围内的元素插入容器中。当且仅当容器中没有具有等价键的元素时,才会插入这些元素。 + +[horizontal] +要求:`value_type` 可以在容器中进行 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可拷贝插入^]。 +抛出:当插入单个元素时,如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:可能使迭代器失效,但仅当插入导致负载大于最大负载时才会发生。 + +--- + +==== 节点插入 +```c++ insert_return_type insert(node_type&& nh); ``` + +如果 `nh` 非空,则当且仅当容器中没有键与 `nh.value()` 等价的元素时,才将关联的元素插入容器。函数返回时 `nh` 变为空。 + +[horizontal] +返回:一个 `insert_return_type` 对象,由 `position`、`inserted` 和 `node` 构造而成: +* 若 `nh` 为空,则 `inserted` 为 `false` , `position` 为 `end()` ,且 `node` 为空。 +* 否则,若插入操作发生,则 `inserted` 为 `true` , `position` 指向被插入的元素,且 `node` 为空。 +* 若插入操作失败,则 `inserted` 为 `false` , `node` 保留 `nh` 的原始值,且 `position` 指向一个键等价于 `nh.value()` 的元素。 +如果 `nh` 非空,则当且仅当容器中不存在键等价于 `nh.value()` 的元素时,插入其关联的元素。函数返回时, `nh` 为空。 + +--- + +==== 带提示的节点插入 +```c++ iterator insert(const_iterator hint, node_type&& nh); ``` + +如果 `nh` 非空,则当且仅当容器中没有键与 `nh.value()` 等价的元素时,才将关联的元素插入容器。如果插入发生,则 `nh` 变为空;否则 `nh` 不变。 + +`hint` 是关于元素插入位置的建议。此实现会忽略该建议。 + +[horizontal] +返回:如果 `nh` 为空,则返回的迭代器为 `end()`。如果插入发生,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出:如果除调用 `hasher` 之外的操作抛出异常,则该函数无效果。 +注意:如果 `nh` 非空且 `nh` 与容器的分配器不相等,则行为未定义。 + +--- + +==== 通过位置擦除 + +[source,c++,subs=+quotes] +---- +_convertible-to-iterator_ erase(iterator position); +_convertible-to-iterator_ erase(const_iterator position); +---- + +擦除由 `position` 指向的元素。 + +[horizontal] +返回;; 返回一个不透明对象,该对象可隐式转换为擦除前紧接在 `position` 之后的 `iterator` 或 `const++_++iterator` 。 抛出;; 无。 注意;; 返回的不透明对象必须被立即丢弃或转换为 `iterator` 或 `const++_++iterator` 。 + +--- + +==== 通过键擦除 +```c++ size_type erase(const key_type& k); template size_type erase(K&& k); ``` + +擦除所有键与 `k` 等价的元素。 + +[horizontal] +返回:被擦除的元素数量。 +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 都不能从 `K` 隐式转换时,才参与重载决议。库假定 `Hash` 可同时用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 范围擦除 + +```c++ iterator erase(const_iterator first, const_iterator last); ``` + +擦除从 `first` 到 `last` 范围内的元素。 + +[horizontal] +返回:被擦除元素之后的迭代器——即 `last`。 +抛出:在此实现中不抛出任何异常(既不调用 `hasher` 也不调用 `key_equal` 对象)。 + +--- + +==== 交换 +```c++ void swap(unordered_node_set& other) noexcept(boost::allocator_traits::is_always_equal::value || boost::allocator_traits::propagate_on_container_swap::value); ``` + +交换容器的内容与参数的内容。 + +如果 `Allocator::propagate_on_container_swap` 被声明且 `Allocator::propagate_on_container_swap::value` 为 `true`,则交换容器的分配器。否则,使用不相等的分配器进行交换将导致未定义行为。 + +[horizontal] +抛出:除非 `key_equal` 或 `hasher` 在交换时抛出异常,否则不抛出任何异常。 + +--- + +==== 通过位置提取 +```c++ node_type extract(const_iterator position); ``` + +提取 `position` 所指向的元素。 + +[horizontal] +返回:一个持有被提取元素的 `node_type` 对象。 +抛出:不抛出任何异常。 + +--- + +==== 通过键提取 +```c++ node_type extract(const key_type& k); template node_type extract(K&& k); ``` + +提取键与 `k` 等价的元素(如果存在)。 + +[horizontal] +返回:一个持有被提取元素的 `node_type` 对象;如果未提取到任何元素,则返回空。 +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时才参与重载决议。库假定 `Hash` 可同时用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== pull +```c++ init_type pull(const_iterator position); ``` + +从 `position` 指向的元素移动构造一个 `init_value` 对象 `x`,擦除该元素并返回 `x`。 + +--- + +==== 清空 +```c++ void clear() noexcept; ``` + +擦除容器中的所有元素。 + +[horizontal] +后置条件:`size() == 0`,`max_load() >= max_load_factor() * bucket_count()` + +--- + +==== 合并 +```c++ template void merge(unordered_node_set& source); template void merge(unordered_node_set&& source); ``` + +从 `source` 转移所有键在 `*this` 中尚未存在的元素节点。 + +[horizontal] +要求:`this->get_allocator() == source.get_allocator()`。 +注意:会使被转移元素的迭代器失效。如果 `*this` 的结果大小大于其原始最大负载,则会使与 `*this` 关联的所有迭代器失效。 + +--- + +=== 观察器 + +==== get_allocator +``` allocator_type get_allocator() const noexcept; ``` + +[horizontal] +返回:容器的分配器。 + +--- + +==== 哈希函数 +```cpp +hasher hash_function() const; +``` + +[horizontal] +返回:容器的哈希函数对象。 + +--- + +==== key_eq +``` key_equal key_eq() const; ``` + +[horizontal] +返回:容器的键相等谓词。 + +--- + +=== 查找 + +==== find +```c++ iterator find(const key_type& k); const_iterator find(const key_type& k) const; template iterator find(const K& k); + +``` + +[horizontal] +返回:指向键与 `k` 等价的元素的迭代器;如果不存在这样的元素,则返回 `end()`。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== count +```c++ size_type count(const key_type& k) const; template size_type count(const K& k) const; ``` + +[horizontal] +返回:键与 `k` 等价的元素数量。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 包含 +```c++ bool contains(const key_type& k) const; template bool contains(const K& k) const; ``` + +[horizontal] +返回:一个布尔值,指示容器中是否存在键等于 `key` 的元素。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== equal_range +```c++ std::pair equal_range(const key_type& k); std::pair equal_range(const key_type& k) const; template std::pair equal_range(const K& k); template std::pair equal_range(const K& k) const; ``` + +[horizontal] +返回:包含所有键与 `k` 等价的元素的范围。如果容器不包含任何此类元素,则返回 `std::make_pair(b.end(), b.end())`。 +注意:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时使用 `K` 和 `Key` 调用,且 `Pred` 是透明的。这支持了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +=== 桶接口 + +==== bucket_count +```c++ size_type bucket_count() const noexcept; ``` + +[horizontal] +返回:桶数组的大小。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ float load_factor() const noexcept; ``` + +[horizontal] +返回:`static_cast(size())/static_cast(bucket_count())`,如果 `bucket_count() == 0` 则返回 `0`。 + +--- + +==== max_load_factor + +```c++ float max_load_factor() const noexcept; ``` + +[horizontal] +返回:容器的最大负载因子。 + +--- + +==== 设置最大负载因子 +```c++ void max_load_factor(float z); ``` + +[horizontal] +效果:不执行任何操作,因为用户不允许更改此参数。保留此函数是为了与 `boost::unordered_set` 保持兼容。 + +--- + + +==== max_load + +```c++ size_type max_load() const noexcept; ``` + +[horizontal] +返回:容器在不触发重哈希的情况下能够容纳的最大元素数量(假设不再有元素被擦除)。 +注意:在构造、重哈希或清空操作之后,容器的最大负载至少为 `max_load_factor() * bucket_count()`。在高负载条件下,该数值可能会因元素擦除而减小。 + +--- + +==== 重哈希 +```c++ void rehash(size_type n); ``` + +必要时更改桶数组的大小,使其至少包含 `n` 个桶,并且使得负载因子小于或等于最大负载因子。在适用的情况下,这将增大或缩小与容器关联的 `bucket_count()`。 + +当 `size() == 0` 时,`rehash(0)` 将释放底层的桶数组。如果提供的分配器使用异形指针,则会随后执行一次默认分配。 + +使迭代器失效并改变元素的顺序。 + +[horizontal] +抛出:如果抛出异常(除非是由容器的哈希函数或比较函数抛出的),则该函数无效果。 + +--- + +==== 保留 +```c++ void reserve(size_type n); ``` + +等价于 `a.rehash(ceil(n / a.max_load_factor()))`。 + +与 `rehash` 类似,此函数可用于增大或缩小容器中的桶数量。 + +使迭代器失效并改变元素的顺序。 + +[horizontal] +抛出:如果抛出异常(除非是由容器的哈希函数或比较函数抛出的),则该函数无效果。 + +--- + +=== 统计信息 + +==== get_stats +```c++ stats get_stats() const; ``` + +[horizontal] +返回:容器迄今为止所执行插入和查找操作的统计描述。 +注意:仅在 xref:reference/stats.adoc#stats[统计计算] 通过 xref:unordered_node_set_boost_unordered_enable_stats[`BOOST_UNORDERED_ENABLE_STATS`] 启用时才可用。 + +--- + +==== reset_stats +```c++ void reset_stats() noexcept; ``` + +[horizontal] +效果:将容器内部维护的统计信息归零。 +注意:仅在 xref:reference/stats.adoc#stats[统计计算] 通过 xref:unordered_node_set_boost_unordered_enable_stats[`BOOST_UNORDERED_ENABLE_STATS`] 启用时才可用。 + +--- + +=== 推导指引 +如果以下任何一条件为真,则推导指引将不参与重载决议: + +- 它具有一个 `InputIterator` 模板参数,并且为该参数推导出的类型不符合输入迭代器的要求。 +- 它具有一个 `Allocator` 模板参数,并且为该参数推导出的类型不符合分配器的要求。 +- 它具有一个 `Hash` 模板参数,并且为该参数推导出的类型是整数类型或符合分配器的要求。 +- 它具有一个 `Pred` 模板参数,并且为该参数推导出的类型符合分配器的要求。 + +推导指引中的 `size_type` 参数类型指的是由该推导指引所推导出的容器类型的 `size_type` 成员类型。其默认值与所选构造函数的默认值一致。 + +==== _iter-value-type_ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-value-type__ = + typename std::iterator_traits::value_type; // exposition only +----- + +=== 相等性比较 + +==== operator +```c++ template bool operator==(const unordered_node_set& x, const unordered_node_set& y); ``` + +若 `x.size() == y.size()` 且对于 `x` 中的每个元素,在 `y` 中均存在一个具有相同键且值相等(使用 `operator==` 比较值类型)的元素,则返回 `true`。 + +[horizontal] +注意:如果两个容器不具有等价的相等谓词,则行为未定义。 + +--- + +==== operator! +```c++ template bool operator!=(const unordered_node_set& x, const unordered_node_set& y); ``` + +若 `x.size() == y.size()` 且对于 `x` 中的每个元素,在 `y` 中均存在一个具有相同键且值相等(使用 `operator==` 比较值类型)的元素,则返回 `false`。 + +[horizontal] +注意:如果两个容器不具有等价的相等谓词,则行为未定义。 + +=== 交换 +```c++ template void swap(unordered_node_set& x, unordered_node_set& y) noexcept(noexcept(x.swap(y))); ``` + +交换 `x` 和 `y` 的内容。 + +如果 `Allocator::propagate_on_container_swap` 被声明且 `Allocator::propagate_on_container_swap::value` 为 `true`,则交换容器的分配器。否则,使用不相等的分配器进行交换将导致未定义行为。 + +[horizontal] +效果:`x.swap(y)` +抛出:除非 `key_equal` 或 `hasher` 在交换时抛出异常,否则不抛出任何异常。 + +--- + +=== erase_if +```c++ template typename unordered_node_set::size_type erase_if(unordered_node_set& c, Predicate pred); ``` + +遍历容器 `c`,并移除所有使给定谓词返回 `true` 的元素。 + +[horizontal] +返回:被擦除的元素数量。 +注意:等价于: +```c++ +auto original_size = c.size(); +for (auto i = c.begin(), last = c.end(); i != last; ) { + if (pred(*i)) { + i = c.erase(i); + } else { + ++i; + } +} +return original_size - c.size(); +``` + +=== 序列化 + +`unordered++_++node++_++set` 可通过本组件库提供的 API,借助 link:../../../../../serialization/index.html[Boost.Serialization] 进行归档/检索。支持常规归档与 XML 归档两种格式。 + +==== 将 `unordered_node_set` 保存到归档中 + +将 `unordered_node_set` 容器 `x` 的所有元素保存到归档(XML 归档)`ar` 中。 + +[horizontal] +要求:`value_type` 必须是可序列化的(对于 XML 归档需支持 XML 序列化),并且支持 Boost.Serialization 的 `save_construct_data`/`load_construct_data` 协议(该协议由满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^] 的类型自动支持)。 + +--- + +==== 从归档中加载 `unordered_node_set` + +删除 `unordered_node_set` 容器 `x` 中所有已存在的元素,并从归档(XML 归档)`ar` 中插入从原始 `unordered_node_set` 容器 `other` 保存到 `ar` 所读取存储中的元素恢复出的副本。 + +[horizontal] +要求;; `value++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入] 要求,且 `x.key++_++equal()` 在功能上需等价于 `other.key++_++equal()` 。 + +--- + +==== 将迭代器/常量迭代器保存到归档 + +将迭代器(或常量迭代器)`it` 的位置信息保存到归档(XML 归档)`ar` 中。`it` 可以是一个 `end()` 迭代器。 + +[horizontal] +要求:`it` 所指向的 `unordered_node_set` 容器 `x` 必须先被保存到 `ar` 中,并且在保存 `x` 和保存 `it` 之间,不能对 `x` 执行任何修改操作。 + +--- + +==== 从归档加载迭代器/常量迭代器 + +使迭代器(或常量迭代器)`it` 指向原始迭代器(或常量迭代器)被保存到归档(XML 归档)`ar` 所读取存储中的位置恢复后的位置。 + +[horizontal] +要求:如果 `x` 是 `it` 所指向的 `unordered_node_set` 容器,则在加载 `x` 和加载 `it` 之间,不能对 `x` 执行任何修改操作。 diff --git a/doc/modules/ROOT/pages/reference/unordered_set_zh_Hans.adoc b/doc/modules/ROOT/pages/reference/unordered_set_zh_Hans.adoc new file mode 100644 index 0000000..84e6f7f --- /dev/null +++ b/doc/modules/ROOT/pages/reference/unordered_set_zh_Hans.adoc @@ -0,0 +1,1182 @@ +[#unordered_set] +== 类模板 unordered_set + +:idprefix: unordered_set_ + +`boost::unordered++_++set` — 一种用于存储唯一值的无序关联容器。 + +=== 概要 + +[listing,subs="+macros,+quotes"] +----- +// #include xref:reference/header_unordered_set.adoc[] + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator> + class unordered_set { + public: + // types + using key_type = Key; + using value_type = Key; + using hasher = Hash; + using key_equal = Pred; + using allocator_type = Allocator; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + using iterator = _implementation-defined_; + using const_iterator = _implementation-defined_; + using local_iterator = _implementation-defined_; + using const_local_iterator = _implementation-defined_; + using node_type = _implementation-defined_; + using insert_return_type = _implementation-defined_; + + // construct/copy/destroy + xref:#unordered_set_default_constructor[unordered_set](); + explicit xref:#unordered_set_bucket_count_constructor[unordered_set](size_type n, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + template + xref:#unordered_set_iterator_range_constructor[unordered_set](InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#unordered_set_copy_constructor[unordered_set](const unordered_set& other); + xref:#unordered_set_move_constructor[unordered_set](unordered_set&& other); + template + xref:#unordered_set_iterator_range_constructor_with_allocator[unordered_set](InputIterator f, InputIterator l, const allocator_type& a); + explicit xref:#unordered_set_allocator_constructor[unordered_set](const Allocator& a); + xref:#unordered_set_copy_constructor_with_allocator[unordered_set](const unordered_set& other, const Allocator& a); + xref:#unordered_set_move_constructor_with_allocator[unordered_set](unordered_set&& other, const Allocator& a); + xref:#unordered_set_initializer_list_constructor[unordered_set](std::initializer_list il, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); + xref:#unordered_set_bucket_count_constructor_with_allocator[unordered_set](size_type n, const allocator_type& a); + xref:#unordered_set_bucket_count_constructor_with_hasher_and_allocator[unordered_set](size_type n, const hasher& hf, const allocator_type& a); + template + xref:#unordered_set_iterator_range_constructor_with_bucket_count_and_allocator[unordered_set](InputIterator f, InputIterator l, size_type n, const allocator_type& a); + template + xref:#unordered_set_iterator_range_constructor_with_bucket_count_and_hasher[unordered_set](InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); + xref:#unordered_set_initializer_list_constructor_with_allocator[unordered_set](std::initializer_list il, const allocator_type& a); + xref:#unordered_set_initializer_list_constructor_with_bucket_count_and_allocator[unordered_set](std::initializer_list il, size_type n, const allocator_type& a); + xref:#unordered_set_initializer_list_constructor_with_bucket_count_and_hasher_and_allocator[unordered_set](std::initializer_list il, size_type n, const hasher& hf, + const allocator_type& a); + xref:#unordered_set_destructor[~unordered_set](); + unordered_set& xref:#unordered_set_copy_assignment[operator++=++](const unordered_set& other); + unordered_set& xref:#unordered_set_move_assignment[operator++=++](unordered_set&& other) + noexcept(boost::allocator_traits::is_always_equal::value && + boost::is_nothrow_move_assignable_v && + boost::is_nothrow_move_assignable_v); + unordered_set& xref:#unordered_set_initializer_list_assignment[operator++=++](std::initializer_list il); + allocator_type xref:#unordered_set_get_allocator[get_allocator]() const noexcept; + + // iterators + iterator xref:#unordered_set_begin[begin]() noexcept; + const_iterator xref:#unordered_set_begin[begin]() const noexcept; + iterator xref:#unordered_set_end[end]() noexcept; + const_iterator xref:#unordered_set_end[end]() const noexcept; + const_iterator xref:#unordered_set_cbegin[cbegin]() const noexcept; + const_iterator xref:#unordered_set_cend[cend]() const noexcept; + + // capacity + ++[[nodiscard]]++ bool xref:#unordered_set_empty[empty]() const noexcept; + size_type xref:#unordered_set_size[size]() const noexcept; + size_type xref:#unordered_set_max_size[max_size]() const noexcept; + + // modifiers + template std::pair xref:#unordered_set_emplace[emplace](Args&&... args); + template iterator xref:#unordered_set_emplace_hint[emplace_hint](const_iterator position, Args&&... args); + std::pair xref:#unordered_set_copy_insert[insert](const value_type& obj); + std::pair xref:#unordered_set_move_insert[insert](value_type&& obj); + template std::pair xref:#unordered_set_transparent_insert[insert](K&& k); + iterator xref:#unordered_set_copy_insert_with_hint[insert](const_iterator hint, const value_type& obj); + iterator xref:#unordered_set_move_insert_with_hint[insert](const_iterator hint, value_type&& obj); + template iterator xref:#unordered_set_transparent_insert_with_hint[insert](const_iterator hint, K&& k); + template void xref:#unordered_set_insert_iterator_range[insert](InputIterator first, InputIterator last); + void xref:#unordered_set_insert_initializer_list[insert](std::initializer_list); + + node_type xref:#unordered_set_extract_by_iterator[extract](const_iterator position); + node_type xref:#unordered_set_extract_by_value[extract](const key_type& k); + template node_type xref:#unordered_set_extract_by_value[extract](K&& k); + insert_return_type xref:#unordered_set_insert_with_node_handle[insert](node_type&& nh); + iterator xref:#unordered_set_insert_with_hint_and_node_handle[insert](const_iterator hint, node_type&& nh); + + iterator xref:#unordered_set_erase_by_position[erase](iterator position); + iterator xref:#unordered_set_erase_by_position[erase](const_iterator position); + size_type xref:#unordered_set_erase_by_value[erase](const key_type& k); + template size_type xref:#unordered_set_erase_by_value[erase](K&& k); + iterator xref:#unordered_set_erase_range[erase](const_iterator first, const_iterator last); + void xref:#unordered_set_quick_erase[quick_erase](const_iterator position); + void xref:#unordered_set_erase_return_void[erase_return_void](const_iterator position); + void xref:#unordered_set_swap[swap](unordered_set& other) + noexcept(boost::allocator_traits::is_always_equal::value && + boost::is_nothrow_swappable_v && + boost::is_nothrow_swappable_v); + void xref:#unordered_set_clear[clear]() noexcept; + + template + void xref:#unordered_set_merge[merge](unordered_set& source); + template + void xref:#unordered_set_merge[merge](unordered_set&& source); + template + void xref:#unordered_set_merge[merge](unordered_multiset& source); + template + void xref:#unordered_set_merge[merge](unordered_multiset&& source); + + // observers + hasher xref:#unordered_set_hash_function[hash_function]() const; + key_equal xref:#unordered_set_key_eq[key_eq]() const; + + // set operations + iterator xref:#unordered_set_find[find](const key_type& k); + const_iterator xref:#unordered_set_find[find](const key_type& k) const; + template + iterator xref:#unordered_set_find[find](const K& k); + template + const_iterator xref:#unordered_set_find[find](const K& k) const; + template + iterator xref:#unordered_set_find[find](CompatibleKey const& k, CompatibleHash const& hash, + CompatiblePredicate const& eq); + template + const_iterator xref:#unordered_set_find[find](CompatibleKey const& k, CompatibleHash const& hash, + CompatiblePredicate const& eq) const; + size_type xref:#unordered_set_count[count](const key_type& k) const; + template + size_type xref:#unordered_set_count[count](const K& k) const; + bool xref:#unordered_set_contains[contains](const key_type& k) const; + template + bool xref:#unordered_set_contains[contains](const K& k) const; + std::pair xref:#unordered_set_equal_range[equal_range](const key_type& k); + std::pair xref:#unordered_set_equal_range[equal_range](const key_type& k) const; + template + std::pair xref:#unordered_set_equal_range[equal_range](const K& k); + template + std::pair xref:#unordered_set_equal_range[equal_range](const K& k) const; + + // bucket interface + size_type xref:#unordered_set_bucket_count[bucket_count]() const noexcept; + size_type xref:#unordered_set_max_bucket_count[max_bucket_count]() const noexcept; + size_type xref:#unordered_set_bucket_size[bucket_size](size_type n) const; + size_type xref:#unordered_set_bucket[bucket](const key_type& k) const; + template size_type xref:#unordered_set_bucket[bucket](const K& k) const; + local_iterator xref:#unordered_set_begin_2[begin](size_type n); + const_local_iterator xref:#unordered_set_begin_2[begin](size_type n) const; + local_iterator xref:#unordered_set_end_2[end](size_type n); + const_local_iterator xref:#unordered_set_end_2[end](size_type n) const; + const_local_iterator xref:#unordered_set_cbegin_2[cbegin](size_type n) const; + const_local_iterator xref:#unordered_set_cend_2[cend](size_type n) const; + + // hash policy + float xref:#unordered_set_load_factor[load_factor]() const noexcept; + float xref:#unordered_set_max_load_factor[max_load_factor]() const noexcept; + void xref:#unordered_set_set_max_load_factor[max_load_factor](float z); + void xref:#unordered_set_rehash[rehash](size_type n); + void xref:#unordered_set_reserve[reserve](size_type n); + }; + + // Deduction Guides + template>, + class Pred = std::equal_to>, + class Allocator = std::allocator>> + unordered_set(InputIterator, InputIterator, typename xref:#unordered_set_deduction_guides[__see below__]::size_type = xref:#unordered_set_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_set, Hash, Pred, Allocator>; + + template, class Pred = std::equal_to, + class Allocator = std::allocator> + unordered_set(std::initializer_list, typename xref:#unordered_set_deduction_guides[__see below__]::size_type = xref:#unordered_set_deduction_guides[__see below__], + Hash = Hash(), Pred = Pred(), Allocator = Allocator()) + -> unordered_set; + + template + unordered_set(InputIterator, InputIterator, typename xref:#unordered_set_deduction_guides[__see below__]::size_type, Allocator) + -> unordered_set, + boost::hash>, + std::equal_to>, Allocator>; + + template + unordered_set(InputIterator, InputIterator, Allocator) + -> unordered_set, + boost::hash>, + std::equal_to>, Allocator>; + + template + unordered_set(InputIterator, InputIterator, typename xref:#unordered_set_deduction_guides[__see below__]::size_type, Hash, Allocator) + -> unordered_set, Hash, + std::equal_to>, Allocator>; + + template + unordered_set(std::initializer_list, typename xref:#unordered_set_deduction_guides[__see below__]::size_type, Allocator) + -> unordered_set, std::equal_to, Allocator>; + + template + unordered_set(std::initializer_list, Allocator) + -> unordered_set, std::equal_to, Allocator>; + + template + unordered_set(std::initializer_list, typename xref:#unordered_set_deduction_guides[__see below__]::size_type, Hash, Allocator) + -> unordered_set, Allocator>; + +} // namespace unordered +} // namespace boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_Key_ +|`Key` must be https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the container (i.e. `allocator_traits` can destroy it). + +|_Hash_ +|A unary function object type that acts a hash function for a `Key`. It takes a single argument of type `Key` and returns a value of type `std::size_t`. + +|_Pred_ +|A binary function object that implements an equivalence relation on values of type `Key`. A binary function object that induces an equivalence relation on values of type `Key`. It takes two arguments of type `Key` and returns a value of type bool. + +|_Allocator_ +|An allocator whose value type is the same as the container's value type. +支持使用 https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[异形指针] 的分配器。 + +|=== + +元素被组织到桶中。具有相同哈希码的键存储在同一桶中。 + +桶的数量可以通过调用 insert 自动增加,或者作为调用 rehash 的结果。 + +=== 配置宏 + +==== `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0` + +全局定义此宏以支持加载由 Boost 1.84 之前版本的 Boost 保存到 Boost.Serialization 归档中的 `unordered_set`。 + +=== 类型定义 + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ iterator; +---- + +一个常量迭代器,其值类型为 `value_type`。 + +迭代器类别至少为前向迭代器。 + +可转换为 `const++_++iterator` 。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ const_iterator; +---- + +一个常量迭代器,其值类型为 `value_type`。 + +迭代器类别至少为前向迭代器。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ local_iterator; +---- + +一种迭代器,其值类型、差值类型以及指针和引用类型均与 iterator 相同。 + +`local++_iterator` 对象可用于遍历单个桶内的元素。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ const_local_iterator; +---- + +一种常量迭代器,其值类型、差值类型以及指针和引用类型均与 const++_iterator 相同。 + +`const_local_iterator` 对象可用于遍历单个桶。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ node_type; +---- + +一个用于存放被提取容器元素的类,符合 https://en.cppreference.com/w/cpp/container/node_handle[NodeHandle] 模型。 + +--- + +[source,c++,subs=+quotes] +---- +typedef _implementation-defined_ insert_return_type; +---- + +内部类模板的特化: + +[source,c++,subs=+quotes] +---- +template +struct _insert_return_type_ // name is exposition only +{ + Iterator position; + bool inserted; + NodeType node; +}; +---- + +其中 `Iterator` = `iterator`,`NodeType` = `node_type`。 + +--- + +=== 构造函数 + +==== 默认构造函数 +```c++ unordered_set(); ``` + +使用 `hasher()` 作为哈希函数,`key_equal()` 作为键相等谓词,`allocator_type()` 作为分配器,以及最大负载因子 `1.0` 构造一个空容器。 + +[horizontal] +后置条件:`size() == 0` 要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]要求。 + +--- + +==== 桶数构造函数 +```c++ explicit unordered_set(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); ``` + +explicit unordered_set(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); + +[horizontal] +后置条件:`size() == 0` 要求:如果使用默认值,则 `hasher`、`key_equal` 和 `allocator_type` 需要满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]要求。 + +--- + +==== 迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_set(InputIterator f, InputIterator l, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `eql` 作为键相等性谓词、 `a` 作为分配器,将最大负载因子设为 `1.0` ,并将区间 `++[++f, l)` 中的元素插入其中。 + +[horizontal] +要求;; 若使用默认值,则 `hasher` 、 `key++_++equal` 和 `allocator++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 复制构造函数 +```c++ unordered_set(const unordered_set& other); ``` + +复制构造函数。复制所含元素、哈希函数、谓词、最大负载因子和分配器。 + +若 `Allocator::select_on_container_copy_construction` 存在且具有正确的签名,则将根据其结果来构造分配器。 + +[horizontal] +要求:;; `value_type` 需满足可复制构造要求。 + +--- + +==== 移动构造函数 +```c++ unordered_set(unordered_set&& other); ``` + +移动构造函数。 + +[horizontal] +说明:;; 该实现使用 Boost.Move。要求:;; `value_type` 需满足可移动构造要求。 + +--- + +==== 带分配器的迭代器范围构造函数 +```c++ template unordered_set(InputIterator f, InputIterator l, const allocator_type& a); ``` + +构造一个空容器,使用 `a` 作为分配器、默认的哈希函数和键相等性谓词,并将最大负载因子设为 `1.0` ,然后将 `[f, l)` 范围内的元素插入其中。 + +[horizontal] +要求:`hasher`、`key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]要求。 + +--- + +==== 分配器构造函数 +```c++ explicit unordered_set(const Allocator& a); ``` + +构造一个空容器,使用分配器 `a`。 + +--- + +==== 带分配器的复制构造函数 +```c++ unordered_set(const unordered_set& other, const Allocator& a); ``` + +构造一个容器,移动 `other` 中所包含的元素,并获取其哈希函数、谓词及最大负载因子,但使用分配器 `a` 。 + +--- + +==== 带分配器的移动构造函数 +```c++ unordered_set(unordered_set&& other, const Allocator& a); ``` + +构造一个容器,移动 `other` 中所包含的元素,并获取其哈希函数、谓词及最大负载因子,但使用分配器 `a` 。 + +[horizontal] +说明:该实现使用 Boost.Move。要求:`value_type` 需满足可移动插入要求。 + +--- + +==== 初始化列表构造函数 +[source,c++,subs="+quotes"] +---- +unordered_set(std::initializer_list il, + size_type n = _implementation-defined_, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& a = allocator_type()); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `eql` 作为键相等性谓词、及 `a` 作为分配器,并设置最大负载因子为 `1.0` ,随后将 `il` 中的元素插入该容器。 + +[horizontal] +要求;; 若使用默认值,则 `hasher` 、 `key++_++equal` 和 `allocator++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带分配器的桶数构造函数 +```c++ unordered_set(size_type n, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数(应为默认哈希函数)、默认的键相等性谓词、 `a` 作为分配器,并设置最大负载因子为 `1.0` 。 + +[horizontal] +后置条件:`size() == 0` +要求:`hasher` 和 `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]要求。 + +--- + +==== 带哈希函数和分配器的桶数构造函数 +```c++ unordered_set(size_type n, const hasher& hf, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、默认的键相等性谓词、 `a` 作为分配器,并设置最大负载因子为 `1.0` 。 + +[horizontal] +后置条件:size() == 0 要求:key_equal 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]要求。 + +--- + +==== 带桶数和分配器的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 作为分配器、默认的哈希函数和键相等性谓词,并设置最大负载因子为 `1.0` ,随后将区间 `++[++f, l)` 中的元素插入该容器。 + +[horizontal] +要求:`hasher`、`key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]要求。 + +--- + +==== 带桶数和哈希函数的迭代器范围构造函数 +[source,c++,subs="+quotes"] +---- +template + unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, + const allocator_type& a); +---- + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、默认的键相等性谓词、 `a` 作为分配器,并设置最大负载因子为 `1.0` ,随后将区间 `++[++f, l)` 中的元素插入该容器。 + +[horizontal] +要求;; `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带分配器的初始化列表构造函数 + +```c++ unordered_set(std::initializer_list il, const allocator_type& a); ``` + +使用 `a` 作为分配器构造一个空容器,设置最大负载因子为 1.0,并将 `il` 中的元素插入其中。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]要求。 + +--- + +==== 带桶数和分配器的初始化列表构造函数 + +```c++ unordered_set(std::initializer_list il, size_type n, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `a` 作为分配器,并设置最大负载因子为 1.0,随后将 `il` 中的元素插入该容器。 + +[horizontal] +要求:`hasher` 和 `key_equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造^]要求。 + +--- + +==== 带桶数、哈希函数和分配器的初始化列表构造函数 + +```c++ unordered_set(std::initializer_list il, size_type n, const hasher& hf, const allocator_type& a); ``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、 `a` 作为分配器,设置最大负载因子为 1.0,并将 `il` 中的元素插入其中。 + +[horizontal] +要求;; `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +=== 析构函数 + +```c++ ~unordered_set(); ``` + +[horizontal] +注意:析构函数会应用于每个元素,并且所有内存都会被释放。 + +--- + +=== 赋值操作 + +==== 复制赋值 + +```c++ unordered_set& operator=(const unordered_set& other); ``` + +赋值运算符。该操作会复制容器内的元素、哈希函数、谓词及最大负载因子,但不会复制分配器。 + +若 `Alloc::propagate_on_container_copy_assignment` 存在且 `Alloc::propagate_on_container_copy_assignment::value` 为 `true`,则覆盖分配器;否则,将使用现有分配器创建复制的元素。 + +[horizontal] +要求:;; `value_type` 需满足可复制构造要求。 + +--- + +==== 移动赋值 +```c++ unordered_set& operator=(unordered_set&& other) noexcept(boost::allocator_traits::is_always_equal::value && boost::is_nothrow_move_assignable_v && boost::is_nothrow_move_assignable_v); ``` 移动赋值运算符。 + +若 `Alloc::propagate_on_container_move_assignment` 存在且 `Alloc::propagate_on_container_move_assignment::value` 为 `true`,则覆盖分配器;否则,将使用现有分配器创建移动的元素。 + +[horizontal] +要求:`value_type` 需满足可移动构造要求。 + +--- + +==== 初始化列表赋值 +```c++ unordered_set& operator=(std::initializer_list il); ``` + +将初始化列表中的值赋给容器。所有已存在的元素将被新元素覆盖或销毁。 + +[horizontal] +要求:`value_type` 需满足对容器而言的 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入^] 要求和 https://en.cppreference.com/w/cpp/named_req/CopyAssignable[可复制赋值^] 要求。 + +--- + +=== 迭代器 + +==== begin +```c++ iterator begin() noexcept; const_iterator begin() const noexcept; ``` + +[horizontal] +返回:指向容器第一个元素的迭代器,若容器为空则返回容器尾后迭代器。 + +--- + +==== end +```c++ iterator end() noexcept; const_iterator end() const noexcept; ``` + +[horizontal] +返回:指向容器尾后位置的迭代器。 + +--- + +==== cbegin +```c++ const_iterator cbegin() const noexcept; ``` + +[horizontal] +返回:指向容器第一个元素的 `const_iterator`,若容器为空,则返回容器尾后迭代器。 + +--- + +==== cend +```c++ const_iterator cend() const noexcept; ``` + +[horizontal] +返回:指向容器尾后位置的 `const_iterator`。 + +--- + +=== 大小与容量 + +==== 空 + +```c++ [[nodiscard]] bool empty() const noexcept; ``` + +[horizontal] +返回:`size() == 0` + +--- + +==== 大小 + +```c++ size_type size() const noexcept; ``` + +[horizontal] +返回:`std::distance(begin(), end())` + +--- + +==== max_size + +```c++ size_type max_size() const noexcept; ``` + +[horizontal] +返回:可能的最大容器的 `size()`。 + +--- + +=== 修改器 + +==== 原地构造 +```c++ template std::pair emplace(Args&&... args); ``` + +当且仅当容器中没有等价值的元素时,插入一个使用参数 `args` 构造的对象。 + +[horizontal] +要求:`value_type` 需从 `args` 处满足对 `X` 的 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^] 要求。返回:若执行了插入,则返回类型中的布尔分量为 `true`。+ + 若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。 抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。 说明:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。+ + 指向元素的指针和引用永远不会失效。 + +--- + +==== emplace_hint +```c++ template iterator emplace_hint(const_iterator position, Args&&... args); ``` + +当且仅当容器中没有等价值的元素时,插入一个使用参数 `args` 构造的对象。 + +`position` 是一个关于元素应插入位置的提示。 + +[horizontal] +要求:`value_type` 需从 `args` 处满足对 `X` 的 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^] 要求。返回:若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。说明:标准对提示的含义表述相当模糊。但使用它的唯一实际方式,也是 Boost.Unordered 唯一支持的方式,是将其指向具有相同键的现有元素。+ +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。+ +指向元素的指针和引用永远不会失效。 + +--- + +==== 复制插入 +```c++ std::pair insert(const value_type& obj); ``` + +当且仅当容器中不存在等价键时,将 `obj` 对象插入到容器中。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入^] 要求。返回:若执行了插入,则返回类型中的布尔分量为 `true`。+ +若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。说明:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。+ +指向元素的指针和引用永远不会失效。 + +--- + +==== 移动插入 +```c++ std::pair insert(value_type&& obj); ``` + +当且仅当容器中不存在等价键时,将 `obj` 对象插入到容器中。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^] 要求。返回:若执行了插入,则返回类型中的布尔分量为 `true`。+ +若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。说明:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。+ +指向元素的指针和引用永远不会失效。 + +--- + +==== 透明插入 +```c++ template std::pair insert(K&& k); ``` + +当且仅当容器中不存在等价键的元素时,插入一个由 `std::forward++<++K++>++(k)` 构造的元素。 + +[horizontal] +要求:`value_type` 需从 `k` 处满足对容器的 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^] 要求。返回:若执行了插入,则返回类型中的布尔分量为 `true`。+ +若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。说明:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。+ +指向元素的指针和引用永远不会失效。+ +此重载仅在以下条件下参与重载决议:`Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 均不能从 `K` 隐式转换。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。这实现了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 带提示的复制插入 +```c++ iterator insert(const_iterator hint, const value_type& obj); ``` Inserts `obj` in the container if and only if there is no element in the container with an equivalent key. + +`hint` 是插入元素位置的建议。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入^] 要求。返回:若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。说明:标准对提示的含义表述相当模糊。但使用它的唯一实际方式,也是 Boost.Unordered 唯一支持的方式,是将其指向具有相同键的现有元素。+ +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。+ +指向元素的指针和引用永远不会失效。 + +--- + +==== 带提示的移动插入 +```c++ iterator insert(const_iterator hint, value_type&& obj); ``` + +当且仅当容器中不存在等价键时,将 `obj` 对象插入到容器中。 + +`hint` 是插入元素位置的建议。 + +[horizontal] +要求:`value_type` 需满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入^] 要求。返回:若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。说明:标准对提示的含义表述相当模糊。但使用它的唯一实际方式,也是 Boost.Unordered 唯一支持的方式,是将其指向具有相同键的现有元素。+ +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。+ +指向元素的指针和引用永远不会失效。 + +--- + +==== 带提示的透明插入 +```c++ template iterator insert(const_iterator hint, K&& k); ``` + +当且仅当容器中不存在等价键的元素时,插入一个由 `std::forward++<++K++>++(k)` 构造的元素。 + +`hint` 是插入元素位置的建议。 + +[horizontal] +要求:`value_type` 需从 `k` 处满足对容器的 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^] 要求。返回:若执行了插入,则迭代器指向新插入的元素;否则指向等价的元素。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。说明:标准对 hint 的含义表述相当模糊。但使用它的唯一实际方式,也是 Boost.Unordered 唯一支持的方式,是将其指向具有相同键的现有元素。+ +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。+ +指向元素的指针和引用永远不会失效。+ +此重载仅在以下条件下参与重载决议:`Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 均不能从 `K` 隐式转换。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。这实现了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 迭代器范围插入 +```c++ template void insert(InputIterator first, InputIterator last); ``` + +将元素范围插入容器中。仅当容器中不存在等价键的元素时,才会插入相应元素。 + +[horizontal] +要求:`value_type` 需从 `*first` 处满足对 `X` 的 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[可原位构造^] 要求。抛出:当插入单个元素时,若调用 `hasher` 以外的操作抛出异常,则函数无效果。说明:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。+ +指向元素的指针和引用永远不会失效。 + +--- + +==== 初始化列表插入 +```c++ void insert(std::initializer_list); ``` + +将元素范围插入容器中。仅当容器中不存在等价键的元素时,才会插入相应元素。 + +[horizontal] +要求:`value_type` 需满足对容器而言的 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入^] 要求。抛出:当插入单个元素时,若调用 `hasher` 以外的操作抛出异常,则函数无效果。说明:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。+ +指向元素的指针和引用永远不会失效。 + +--- + +==== 通过迭代器提取 +```c++ node_type extract(const_iterator position); ``` + +移除由 `position` 指向的元素。 + +[horizontal] +返回:一个拥有该元素的 `node_type`。说明:在 C++17 中,通过此方法提取的节点可以插入到兼容的 `unordered_multiset` 中,但该功能尚未支持。 + +--- + +==== 通过键值提取元素 +```c++ node_type extract(const key_type& k); template node_type extract(K&& k); ``` + +移除键等价于 `k` 的元素。 + +[horizontal] +返回:若找到元素,则返回拥有该元素的 `node_type`;否则返回空 `node_type`。抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出异常。说明:在 C++17 中,通过此方法提取的节点可以插入到兼容的 `unordered_multiset` 中,但该功能尚未支持。+ +`template` 重载仅在以下条件下参与重载决议:`Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 均不能从 `K` 隐式转换。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。这实现了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 通过 `node++_++handle` 插入 +```c++ insert_return_type insert(node_type&& nh); ``` + +若 `nh` 为空节点,则此操作不产生任何效果。 + +否则,当且仅当容器中不存在等价键的元素时,才会插入 `nh` 所拥有的元素。 + +[horizontal] +要求:`nh` 为空或 `nh.get_allocator()` 与容器的分配器相等。返回:若 `nh` 为空,则返回一个 `insert_return_type`,其中:`inserted` 为 `false`,`position` 等于 `end()`,`node` 为空。+ +否则若已存在等价的键,则返回一个 `insert_return_type`,其中:`inserted` 为 `false`,`position` 指向匹配的元素,`node` 包含 `nh` 中的节点。+ +否则若插入成功,则返回一个 `insert_return_type`,其中:`inserted` 为 `true`,`position` 指向新插入的元素,`node` 为空。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。说明:可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。+ +指向元素的指针和引用永远不会失效。+ +在 C++17 中,该函数可用于插入从兼容的 `unordered_multiset` 中提取的节点,但该功能尚未支持。 + +--- + +==== 带提示和 `node++_++handle` 的插入 +```c++ iterator insert(const_iterator hint, node_type&& nh); ``` + +若 `nh` 为空节点,则此操作不产生任何效果。 + +否则,当且仅当容器中不存在等价键的元素时,才会插入 `nh` 所拥有的元素。 + +如果容器中已存在具有等价键的元素,则对 `nh` 不产生任何影响.(即 `nh` 仍持有该节点.) + +`hint` 是插入元素位置的建议。 + +[horizontal] +要求:`nh` 为空或 `nh.get_allocator()` 与容器的分配器相等。返回:若 `nh` 为空,则返回 `end()`。+ +若容器中已存在等价的键,则返回指向该元素的迭代器。+ +否则,返回指向新插入元素的迭代器。抛出:若调用 `hasher` 以外的操作抛出异常,则函数无效果。说明:标准对 hint 的含义表述相当模糊。但使用它的唯一实际方式,也是 Boost.Unordered 唯一支持的方式,是将其指向具有相同键的现有元素。+ +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时才会发生。+ +指向元素的指针和引用永远不会失效。+ +该函数可用于插入从兼容的 `unordered_multiset` 中提取的节点。 + +--- + +==== 通过位置擦除 + +```c++ iterator erase(iterator position); iterator erase(const_iterator position); ``` + +擦除由 `position` 指向的元素。 + +[horizontal] +返回:擦除前指向 `position` 之后位置的迭代器。抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出异常。说明:在旧版本中,该操作可能效率较低,因为它需要搜索多个桶才能找到所返回迭代器的位置。数据结构已做修改,此问题已不复存在,并且其他替代的 `erase` 方法已被弃用。 + +--- + +==== 通过值擦除 +```c++ size_type erase(const key_type& k); template size_type erase(K&& k); ``` + +擦除所有键等价于 `k` 的元素。 + +[horizontal] +返回:擦除的元素数量。抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出异常。说明:`template` 重载仅在以下条件下参与重载决议:`Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef,且 `iterator` 和 `const_iterator` 均不能从 `K` 隐式转换。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。这实现了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 范围擦除 + +```c++ iterator erase(const_iterator first, const_iterator last); ``` + +擦除从 `first` 到 `last` 范围内(包含 `first` ,不包含 `last` )的元素。 + +[horizontal] +返回:指向被擦除元素之后位置的迭代器,即 `last`。抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出异常。+ + 在此实现中,此重载不会调用这两个函数对象的任何方法,因此不会抛出异常,但在其他实现中可能并非如此。 + +--- + +==== quick_erase +```c++ void quick_erase(const_iterator position); ``` + +擦除由 `position` 指向的元素。 + +[horizontal] +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出异常。 ++ + 在此实现中,此重载不会调用这两个函数对象的任何方法,因此不会抛出异常,但在其他实现中可能并非如此。 +说明:此方法最初实现的原因是,从 `erase` 返回指向下一个元素的迭代器开销较大,但容器已经过重新设计,该问题已不复存在。因此,此方法现已弃用。 + +--- + +==== erase_return_void +```c++ void erase_return_void(const_iterator position); ``` + +擦除由 `position` 指向的元素。 + +[horizontal] +抛出:仅当 `hasher` 或 `key_equal` 抛出异常时才会抛出异常。 ++ + 在此实现中,此重载不会调用这两个函数对象的任何方法,因此不会抛出异常,但在其他实现中可能并非如此。 +说明:此方法最初实现的原因是,从 `erase` 返回指向下一个元素的迭代器开销较大,但容器已经过重新设计,该问题已不复存在。因此,此方法现已弃用。 + +--- + +==== 交换 +```c++ void swap(unordered_set& other) noexcept(boost::allocator_traits::is_always_equal::value && boost::is_nothrow_swappable_v && boost::is_nothrow_swappable_v); ``` + +交换容器与参数的内容。 + +如果声明了 `Allocator::propagate++_++on++_++container++_++swap` 且 `Allocator::propagate++_++on++_++container++_++swap::value` 为 `true` ,则交换容器的分配器。则,在分配器不相等的情况下进行交换将导致未定义行为。 + +[horizontal] +抛出:除非 `key_equal` 或 `hasher` 的复制构造函数或复制赋值运算符抛出异常,否则不会抛出异常。说明:由于相等谓词和哈希函数是通过它们的复制构造函数进行交换的,因此异常规范与 C++11 标准并不完全相同。 + +--- + +==== 清空 +```c++ void clear() noexcept; ``` + +擦除容器中的所有元素。 + +[horizontal] +后置条件:`size() == 0` 抛出:永不抛出异常。 + +--- + +==== 合并 +```c++ template void merge(unordered_set& source); template void merge(unordered_set&& source); template void merge(unordered_multiset& source); template void merge(unordered_multiset&& source); ``` + +通过遍历 `source` 容器,提取其中所有不包含在 `++*++this` 中的节点,并将其插入到 `++*++this` 中,以此尝试将两个容器"合并"。 + +由于 `source` 可以有不同的哈希函数和键相等性谓词,因此会使用 `this-++>++hash++_++function()` 操作对 `source` 中每个节点的键进行重哈希,并在需要时使用 `this-++>++key++_++eq()` 进行比较。 + +如果 `this-++>++get++_++allocator() != source.get++_++allocator()` ,则此函数的行为未定义。 + +此函数不会复制或移动任何元素,而是仅将节点从 `source` 重新定位到 `*this` 中。 + +[horizontal] +说明:+ -- +* 指向被转移元素的指针和引用保持有效。 +* 使指向被转移元素的迭代器失效。 +* 使属于 `++*++this` 的迭代器失效。 +* 指向 `source` 中未被转移元素的迭代器保持有效。 +-- + +--- + +=== 观察器 + +==== get++_++allocator +``` allocator_type get_allocator() const; ``` + +--- + +==== 哈希函数 +``` hasher hash_function() const; ``` + +[horizontal] +返回:容器的哈希函数。 + +--- + +==== key++_++eq + +``` key_equal key_eq() const; ``` + +[horizontal] +返回:容器的键相等谓词。 + +--- + +=== 查找 + +==== find +```c++ iterator find(const key_type& k); const_iterator find(const key_type& k) const; template iterator find(const K& k); template const_iterator find(const K& k) const; template iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq); template const_iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const; ``` + +[horizontal] +返回:指向键与 `k` 等价的元素的迭代器,若不存在这样的元素则返回 `b.end()`。说明:包含 `CompatibleKey`、`CompatibleHash` 和 `CompatiblePredicate` 的模板化重载是非标准扩展,允许您使用兼容的哈希函数和相等谓词来处理不同类型的键,以避免昂贵的类型转换。通常不鼓励使用它们,而应使用 `K` 成员函数模板。+ +`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。这实现了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== count +```c++ size_type count(const key_type& k) const; template size_type count(const K& k) const; ``` + +[horizontal] +返回:键与 `k` 等价的元素数量。说明:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。这实现了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== 包含 +```c++ bool contains(const key_type& k) const; template bool contains(const K& k) const; ``` + +[horizontal] +返回:一个布尔值,指示容器中是否存在键等于 `key` 的元素。说明:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。这实现了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== equal++_++range +```c++ std::pair equal_range(const key_type& k); std::pair equal_range(const key_type& k) const; template std::pair equal_range(const K& k); template std::pair equal_range(const K& k) const; ``` + +[horizontal] +返回:包含所有键与 `k` 等价的元素的范围。若容器中不包含任何此类元素,则返回 `std::make_pair(b.end(), b.end())`。说明:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。这实现了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +=== 桶接口 + +==== bucket++_++count +```c++ size_type bucket_count() const noexcept; ``` + +[horizontal] +返回:桶的数量。 + +--- + +==== max_bucket_count +```c++ size_type max_bucket_count() const noexcept; ``` + +[horizontal] +返回:桶数的上限。 + +--- + +==== 桶大小 +```c++ size_type bucket_size(size_type n) const; ``` + +[horizontal] +要求:`n < bucket_count()` 返回:桶 `n` 中的元素数量。 + +--- + +==== 桶 +```c++ size_type bucket(const key_type& k) const; template size_type bucket(const K& k) const; ``` + +[horizontal] +返回:将包含键为 `k` 的元素的桶索引。后置条件:返回值小于 `bucket_count()`。说明:`template` 重载仅在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员 typedef 时参与重载决议。库假定 `Hash` 可同时以 `K` 和 `Key` 调用,且 `Pred` 是透明的。这实现了异构查找,从而避免了实例化 `Key` 类型对象的开销。 + +--- + +==== begin + +```c++ local_iterator begin(size_type n); const_local_iterator begin(size_type n) const; ``` + +[horizontal] +要求:`n` 应在 `[0, bucket_count())` 范围内。返回:指向索引为 `n` 的桶中第一个元素的局部迭代器。 + +--- + +==== end +```c++ local_iterator end(size_type n); const_local_iterator end(size_type n) const; ``` + +[horizontal] +要求:`n` 应在 `[0, bucket_count())` 范围内。返回:指向索引为 `n` 的桶中“尾后”元素的局部迭代器。 + +--- + +==== cbegin +```c++ const_local_iterator cbegin(size_type n) const; ``` + +[horizontal] +要求:`n` 应在 `[0, bucket_count())` 范围内。返回:指向索引为 `n` 的桶中第一个元素的常量局部迭代器。 + +--- + +==== cend +```c++ const_local_iterator cend(size_type n) const; ``` + +[horizontal] +要求:`n` 应在 `[0, bucket_count())` 范围内。返回:指向索引为 `n` 的桶中“尾后”元素的常量局部迭代器。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ float load_factor() const noexcept; ``` + +[horizontal] +返回:每个桶的平均元素数量。 + +--- + +==== max_load_factor + +```c++ float max_load_factor() const noexcept; ``` + +[horizontal] +返回:当前最大负载因子。 + +--- + +==== 设置max_load_factor(最大负载因子) +```c++ void max_load_factor(float z); ``` + +[horizontal] +效果:使用 `z` 作为提示,更改容器的最大负载因子。 + +--- + +==== 重哈希 +```c++ void rehash(size_type n); ``` + +改变桶的数量,使其至少为 `n` 个,并确保负载因子小于或等于最大负载因子。此操作将根据情况增加或减少容器关联的 `bucket_count()` 。 + +当 `size() == 0` 时,`rehash(0)` 将释放底层桶数组。 + +使迭代器失效,并改变元素的顺序。指向元素的指针和引用不会失效。 + +[horizontal] +抛出:若抛出异常(除非由容器的哈希函数或比较函数抛出),则该函数无效果。 + +--- + +==== 保留 +```c++ void reserve(size_type n); ``` + +等价于 `a.rehash(ceil(n / a.max_load_factor()))`,若 `n > 0` 且 `a.max_load_factor() == std::numeric_limits::infinity()` 则等价于 `a.rehash(1)`。 + +与 `rehash` 类似,该函数可用于增加或减少容器中的桶数量。 + +使迭代器失效,并改变元素的顺序。指向元素的指针和引用不会失效。 + +[horizontal] +抛出:若抛出异常(除非由容器的哈希函数或比较函数抛出),则该函数无效果。 + + +=== 推导指引 +如果以下任何一条件为真,则推导指引将不参与重载决议: + +- 该推导指引包含 `InputIterator` 模板参数,且为此参数推导出的类型不符合输入迭代器的要求。 - 该推导指引包含 `Allocator` 模板参数,且为该参数推导出的类型不符合分配器要求。 - 该推导指引包含 `Hash` 模板参数,且为该参数推导出的类型为整型或符合分配器要求。 - 该推导指引包含 `Pred` 模板参数,且为该参数推导出的类型符合分配器要求。 + +推导指引中的 `size++_++type` 参数类型,指向由该推导指引所推导容器类型的 `size++_++type` 成员类型。其默认值与所选构造函数的默认值一致。 + +==== __iter-value-type__ +[listings,subs="+macros,+quotes"] +----- +template + using __iter-value-type__ = + typename std::iterator_traits::value_type; // exposition only +----- + +=== 相等性比较 + +==== operator +```c++ template bool operator==(const unordered_set& x, const unordered_set& y); ``` + +如果 `x.size() == y.size()` 且对于 `x` 中的每个元素,在 `y` 中均存在一个具有相同键且值相等(使用 `operator==` 比较值类型)的元素,则返回 `true`。 + +[horizontal] +说明:如果两个容器的相等谓词不等价,则行为未定义。 + +--- + +==== operator! +```c++ template bool operator!=(const unordered_set& x, const unordered_set& y); ``` + +如果 `x.size() == y.size()` 且对于 `x` 中的每个元素,在 `y` 中均存在一个具有相同键且值相等(使用 `operator==` 比较值类型)的元素,则返回 `false`。 + +[horizontal] +说明:如果两个容器的相等谓词不等价,则行为未定义。 + +--- + +=== 交换 +```c++ template void swap(unordered_set& x, unordered_set& y) noexcept(noexcept(x.swap(y))); ``` + +交换 `x` 和 `y` 的内容。 + +如果声明了 `Allocator::propagate++_++on++_++container++_++swap` 且 `Allocator::propagate++_++on++_++container++_++swap::value` 为 `true` ,则交换容器的分配器。则,在分配器不相等的情况下进行交换将导致未定义行为。 + +[horizontal] +效果:`x.swap(y)` 抛出:除非 `key_equal` 或 `hasher` 的复制构造函数或复制赋值运算符抛出异常,否则不会抛出异常。 注意:由于相等谓词和哈希函数是通过它们的复制构造函数进行交换的,因此异常规范与 C++11 标准并不完全相同。 + +--- + +=== erase_if +```c++ template typename unordered_set::size_type erase_if(unordered_set& c, Predicate pred); ``` + +遍历容器 `c`,并移除所有使得给定谓词返回 `true` 的元素。 + +[horizontal] +返回:被擦除的元素数量。注意:等价于:auto original_size = c.size(); for (auto i = c.begin(), last = c.end(); i != last; ) { if (pred(*i)) { i = c.erase(i); } else { ++i; } } return original_size - c.size(); + +=== 序列化 + +`unordered++_++set` 可通过本库提供的 API,借助 link:../../../../../serialization/index.html[Boost.Serialization] 实现序列化存档与读取功能。同时支持常规格式与 XML 格式的归档。 + +==== 将 unordered++_++set 保存到归档 + +将 `unordered++_++set` 对象 `x` 的所有元素保存到归档(XML 归档) `ar` 中。 + +[horizontal] +要求;; `value++_++type` 必须可序列化(支持 XML 序列化),且需要支持 Boost.Serialization 的 `save++_++construct++_++data` / `load++_++construct++_++data` 协议(该协议自动支持满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求的类型)。 + +--- + +==== 从归档加载 unordered++_++set + +删除 `unordered++_++set` 对象 `x` 的所有预先存在的元素,并从归档(XML 归档) `ar` 中读取原始 `unordered++_++set` 对象 `other` 先前保存至存储的元素副本并插入到 `x` 中。 + +[horizontal] +要求;; `value++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入] 要求,且 `x.key++_++equal()` 在功能上需等价于 `other.key++_++equal()` 。 注意;; 若归档文件是使用 Boost 1.84 之前的版本保存的,则必须全局定义配置宏 `BOOST++_++UNORDERED++_++ENABLE++_++SERIALIZATION++_++COMPATIBILITY++_++V0` 才能成功执行此操作;否则将抛出异常。 + +--- + +==== 将迭代器/常量迭代器保存到归档 + +将 `iterator` ( `const++_++iterator` )常量迭代器 `it` 的位置信息保存到归档(XML 归档) `ar` 中。 `it` 可以是 `end()` 迭代器。 + +[horizontal] +要求;; 迭代器 `it` 指向的 `unordered++_++set` `x` 必须已预先保存至归档 `ar` ,且在保存 `x` 和保存 `it` 的时间间隔内 `x` 执行任何修改操作。 + +--- + +==== 从归档加载迭代器/常量迭代器 + +使 `iterator` ( `const++_++iterator` ) `it` 指向原始 `iterator` ( `const++_++iterator` )所恢复的位置。该原始迭代器已被保存到由归档(XML 归档) `ar` 读取的存储中。 + +[horizontal] +要求;; 如果 `x` 是 `it` 指向的 `unordered++_++set` ,则在加载 `x` 与加载 `it` 的时间间隔内,不得对 `x` 执行任何修改操作。 diff --git a/doc/modules/ROOT/pages/regular_zh_Hans.adoc b/doc/modules/ROOT/pages/regular_zh_Hans.adoc new file mode 100644 index 0000000..09a185d --- /dev/null +++ b/doc/modules/ROOT/pages/regular_zh_Hans.adoc @@ -0,0 +1,168 @@ +[#regular] = 常规容器 + +:idprefix: regular_ + +Boost.Unordered 的闭寻址容器( `boost::unordered++_++set` 、 `boost::unordered++_++map` 、 `boost::unordered++_++multiset` 与 `boost::unordered++_++multimap` )完全符合 C{plus}{plus} 无序关联容器的标准规范。因此,对于熟悉 `std::unordered++_++set` 、 `std::unordered++_++map` 等用法的用户,可直接将其替换为Boost.Unordered 中的同名容器。开放寻址容器( `boost::unordered++_++node++_++set` 、 `boost::unordered++_++node++_++map` 、 `boost::unordered++_++flat++_++set` 和 `boost::unordered++_++flat++_++map` )的接口与之高度相似,但存在一些细微差异,详见专门的 xref:compliance.adoc#compliance_open_addressing_containers[标准符合性章节] 。 + + +对于没有哈希容器经验但熟悉常规关联容器( `std::set` 、 `std::map` 、 `std::multiset` 和 `std::multimap` )的读者,Boost.Unordered 容器的使用方式与之类似: + +[source,cpp] +---- +typedef boost::unordered_map map; +map x; +x["one"] = 1; +x["two"] = 2; +x["three"] = 3; + +assert(x.at("one") == 1); +assert(x.find("missing") == x.end()); +---- + +但由于元素不按顺序存储,以下代码的输出结果: + +[source,c++] +---- +for(const map::value_type& i: x) { + std::cout<`, `>=` operators. +|Can be compared using the `==` and `!=` operators. + +| +|When inserting with a hint, implementations are permitted to ignore the hint. + +|=== + +--- + +[caption=, title='Table {counter:table-counter} Complexity Guarantees'] +[cols="1,1,1", frame=all, grid=rows] +|=== +|Operation |Associative Containers |Unordered Associative Containers + +|Construction of empty container +|constant +|O(_n_) where _n_ is the minimum number of buckets. + +|Construction of container from a range of _N_ elements +|O(_N log N_), O(_N_) if the range is sorted with `value_comp()` +|Average case O(_N_), worst case O(_N^2^_) + +|Insert a single element +|logarithmic +|Average case constant, worst case linear + +|Insert a single element with a hint +|Amortized constant if `t` elements inserted right after hint, logarithmic otherwise +|Average case constant, worst case linear (ie. the same as a normal insert). + +|Inserting a range of _N_ elements +|_N_ log(`size()` + _N_) +|Average case O(_N_), worst case O(_N_ * `size()`) + +|Erase by key, `k` +|O(log(`size()`) + `count(k)`) +|Average case: O(`count(k)`), Worst case: O(`size()`) + +|Erase a single element by iterator +|Amortized constant +|Average case: O(1), Worst case: O(`size()`) + +|Erase a range of _N_ elements +|O(log(`size()`) + _N_) +|Average case: O(_N_), Worst case: O(`size()`) + +|Clearing the container +|O(`size()`) +|O(`size()`) + +|Find +|logarithmic +|Average case: O(1), Worst case: O(`size()`) + +|Count +|O(log(`size()`) + `count(k)`) +|Average case: O(1), Worst case: O(`size()`) + +|`equal_range(k)` +|logarithmic +|Average case: O(`count(k)`), Worst case: O(`size()`) + +|`lower_bound`,`upper_bound` +|logarithmic +|n/a + +|=== diff --git a/doc/modules/ROOT/pages/structures_zh_Hans.adoc b/doc/modules/ROOT/pages/structures_zh_Hans.adoc new file mode 100644 index 0000000..a00d49e --- /dev/null +++ b/doc/modules/ROOT/pages/structures_zh_Hans.adoc @@ -0,0 +1,112 @@ +[#structures]= 数据结构 + +:idprefix: structures_ + +== 闭寻址容器 + +++++ + +++++ + +Boost.Unordered 提供了速度最快的闭寻址实现之一,该结构通常被称为 https://en.wikipedia.org/wiki/Hash_table#Separate_chaining[分离链接法] 。其数据结构示例如下: + +[#img-bucket-groups,.text-center] +.A simple bucket group approach +image::bucket-groups.png[align=center] + +系统分配一个"桶"数组,其中每个桶分别指向其专属的链表。这种结构使得桶迭代能直接满足标准要求。但采用此布局进行整体容器迭代通常较慢,因为需检查每个桶的占用状态,其时间复杂度为 `O(bucket++_++count() {plus} size())` ,而标准要求复杂度应为 `O(size())` 。 + +典型的标准库实现最终会形成如下图所示的结构: + +[.text-center] +.The canonical standard approach +image::singly-linked.png[align=center,link=_images/singly-linked.png,window=_blank] + +值得注意的是,这种方法仅被 pass:[libc++] 和 pass:[libstdc++] 所采用;MSVC 的 Dinkumware 实现则使用了不同的方法。关于标准容器的更详细分析可参见 http://bannalia.blogspot.com/2013/10/implementation-of-c-unordered.html[此处]。 + +这种非常规数据结构通过将所有节点连成单向链表来实现高效全局迭代。需注意:桶指向其元素起始节点的__前驱节点__,这样设计的目的是在保持高效元素删除的同时,无需引入双向链表。但这种数据结构不可避免地带来了额外的间接访问开销。例如,要访问某个桶的第一个元素,必须执行类似以下操作: + +```c++ auto const idx = get_bucket_idx(hash_function(key)); node* p = buckets[idx]; // first load node* n = p->next; // second load if (n && is_in_bucket(n, idx)) { value_type const& v = *n; // third load // ... } ``` + +使用简单的桶组布局,只需执行以下操作:```c++auto const idx = get_bucket_idx(hash_function(key));node* n = buckets[idx]; // 第一次加载if (n) { value_type const& v = *n; // 第二次加载 // ...}``` + +在实际使用中,额外的间接层会对 `insert`、`find` 和 `erase` 等常见操作的性能产生显著影响。但为了保持容器的迭代速度,Boost.Unordered 引入了一种新颖的数据结构——“桶组”。桶组是桶数组的一个固定宽度的子区间视图。它包含一个位掩码(类型为 `std::size_t`),用于跟踪桶的占用情况,并包含两个指针,以便与非空组形成双向链表。下面是一个示例图: + +[#img-fca-layout] +.The new layout used by Boost +image::fca.png[align=center] + +因此,容器范围内的迭代转变为遍历非空的桶组(这是一个具有常数时间复杂度的操作),从而将时间复杂度降回 `O(size())`。总体而言,一个桶组的大小仅为 4 个字,它覆盖 `sizeof(std::size_t) * CHAR_BIT` 个桶,这意味着在所有常见实现中,每个桶由桶组引入的空间开销仅为 4 位。 + +关于 Boost.Unordered 闭地址实现的更详细描述,请参阅 https://bannalia.blogspot.com/2022/06/advancing-state-of-art-for.html[外部文章]。有关实现原理的更多信息,请阅读 xref:rationale.adoc#rationale_closed_addressing_containers[相应章节]。 + +== 开放寻址容器 + +该图展示了 `boost::unordered_flat_set`/`unordered_node_set` 和 `boost::unordered_flat_map`/`unordered_node_map` 的基本内部布局。 + + +[#img-foa-layout] +.Open-addressing layout used by Boost.Unordered. +image::foa.png[align=center] + +与所有开放寻址容器一样,元素(对于 `boost::unordered++_++node++_++set` 和 `boost::unordered++_++node++_++map`则是元素节点的指针)直接存储在桶数组中。 该数组在逻辑上被划分为 2^_n_^个组,每组包含 15个元素。 除桶数组外,还存在一个关联的__元数据数组__,其中包含 2^_n_^ 个 16 字节的字。 + +[#img-foa-metadata] +.Breakdown of a metadata word. +image::foa-metadata.png[align=center] + +一个元数据字被划分为 15 个 _h_~_i_~ 字节(每个字节对应一个关联的桶),以及一个 _溢出字节_(图中标记为 _ofw_)。_h_~_i_~ 的值计算方式如下: + +- 0 表示对应的桶为空。 +- 1 用于编码一种特殊的空桶,称为 _哨位桶_,在容器被完全遍历时内部用于停止迭代。 +- 如果桶被占用,则存储从元素哈希值中获得的 _缩减哈希值_。 + +当查找一个哈希值为 _h_ 的元素时,可以使用诸如 https://en.wikipedia.org/wiki/SSE2[SSE2] 和 https://en.wikipedia.org/wiki/ARM_architecture_family#Advanced_SIMD_(Neon)[Neon] 等 SIMD 技术,仅需少量 CPU 指令即可快速检查完整的元数据字,并在全部 15 个桶中寻找 _h_ 的缩减值:不匹配的桶可以迅速丢弃,而那些缩减哈希值匹配的桶则需要通过与对应元素的完整比较来进行检查。如果未找到目标元素,则检查溢出字节。 + +- 若位置 _h_ mod 8 的比特位为零,则查找终止(确认元素不存在)。 元素不存在)。 - 若该比特位为1(表示该组已__溢出__),则通过 https://en.wikipedia.org/wiki/Quadratic_probing[_二次探查法_] 检查后续组,并重复此过程。 + +插入操作的算法逻辑类似:通过 SIMD 技术定位空桶,当遍历到已满的组时,会将其对应的溢出位设置为 1。 + +在不支持 SIMD 的架构中,逻辑布局保持不变,但元数据字采用一种称为 _位交错_ 的技术进行编码:这种布局允许我们仅使用标准算术和逻辑运算,以相当不错的性能来模拟 SIMD。 + +[#img-foa-metadata-interleaving] +.Bit-interleaved metadata word. +image::foa-metadata-interleaving.png[align=center] + +关于 Boost.Unordered 开放寻址实现的更详细描述,请参阅 https://bannalia.blogspot.com/2022/11/inside-boostunorderedflatmap.html[外部文章]。有关实现原理的更多信息,请阅读 xref:rationale.adoc#rationale_open_addresing_containers[相应章节]。 + +== 并发容器 + +`boost::concurrent_flat_set`/`boost::concurrent_node_set` 和 `boost::concurrent_flat_map`/`boost::concurrent_node_map` 使用了上述基本的 xref:structures.adoc#structures_open_addressing_containers[开放寻址布局],并增加了同步机制。 + + +[#img-cfoa-layout] +.Concurrent open-addressing layout used by Boost.Unordered. +image::cfoa.png[align=center] + +采用两级同步机制: + +* 容器层面:使用读写互斥锁来控制来自任何操作的访问。 +对容器的访问。通常情况下,即使是修改操作,此类访问也处于读模式(即并发的),因此在实际使用中,此层面几乎不会发生线程争用。仅在重哈希或执行交换、赋值等容器级操作时,访问才会处于写模式(阻塞)。 +* 组级:每个包含15个槽位的组都配备一个8字节的字,其中包含: +- 一个读写自旋锁,用于同步对组内任何元素的访问。 +- 一个原子 _插入计数器_,用于下文所述的乐观插入。 + +通过使用原子操作访问组元数据,查找操作在(组级别)上是无锁的,直到需要与实际进行 SIMD 匹配的元素进行比较时才会使用组的自旋锁。 + +插入操作使用以下 _乐观算法_: + +* 探测起始组的插入计数器值 +序列会被本地记录(我们称此值为 `c0`)。 +* 查找过程如上所述。如果查找未找到等效元素, +则会依次对探测序列中的每个组进行加锁/解锁,以搜索可用于插入的空槽位。 +* 当定位到一个可用槽位时,该槽位会被预先占用(其 +缩减哈希值被设置),并且插入计数器被原子地递增:如果在整个操作过程中没有其他线程递增该计数器(通过与 `c0` 比较来检查),则插入操作可以顺利完成,否则将回滚并重新开始。 + +该算法在查找和实际插入阶段都具有非常低的争用,代价是如果其他线程在同一个组上成功执行了插入操作,则当前计算可能需要重新开始。在实际应用中,重新开始的频率极低,根据我们的一些基准测试,其量级在百万分之一以内。 + +如需了解实现原理的更多内容,请参阅 xref:rationale.adoc#rationale_concurrent_containers [对应章节]。