diff --git a/doc/modules/ROOT/nav_zh_Hans.adoc b/doc/modules/ROOT/nav_zh_Hans.adoc new file mode 100644 index 0000000..86f087b --- /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[`++<++boost/unordered/unordered++_++map++_++fwd.hpp++>++`] +** xref:reference/header_unordered_map_top.adoc[`++<++boost/unordered++_++map.hpp++>++`] +** xref:reference/header_unordered_map.adoc[`++<++boost/unordered/unordered++_++map.hpp++>++`] +** xref:reference/unordered_map.adoc[`unordered_map`] +** xref:reference/unordered_multimap.adoc[`unordered_multimap`] +** xref:reference/header_unordered_set_fwd.adoc[`++<++boost/unordered/unordered++_++set++_++fwd.hpp++>++`] +** xref:reference/header_unordered_set_top.adoc[`++<++boost/unordered++_++set.hpp++>++`] +** xref:reference/header_unordered_set.adoc[`++<++boost/unordered/unordered++_++set.hpp++>++`] +** 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[`++<++boost/unordered/unordered++_++flat++_++map++_++fwd.hpp++>++`] +** xref:reference/header_unordered_flat_map.adoc[`++<++boost/unordered/unordered++_++flat++_++map.hpp++>++`] +** xref:reference/unordered_flat_map.adoc[`unordered_flat_map`] +** xref:reference/header_unordered_flat_set_fwd.adoc[`++<++boost/unordered/unordered++_++flat++_++set++_++fwd.hpp++>++`] +** xref:reference/header_unordered_flat_set.adoc[`++<++boost/unordered/unordered++_++flat++_++set.hpp++>++`] +** xref:reference/unordered_flat_set.adoc[`unordered_flat_set`] +** xref:reference/header_unordered_node_map_fwd.adoc[`++<++boost/unordered/unordered++_++node++_++map++_++fwd.hpp++>++`] +** xref:reference/header_unordered_node_map.adoc[`++<++boost/unordered/unordered++_++node++_++map.hpp++>++`] +** xref:reference/unordered_node_map.adoc[`unordered_node_map`] +** xref:reference/header_unordered_node_set_fwd.adoc[`++<++boost/unordered/unordered++_++node++_++set++_++fwd.hpp++>++`] +** xref:reference/header_unordered_node_set.adoc[`++<++boost/unordered/unordered++_++node++_++set.hpp++>++`] +** xref:reference/unordered_node_set.adoc[`unordered_node_set`] +** xref:reference/header_concurrent_flat_map_fwd.adoc[`++<++boost/unordered/concurrent++_++flat++_++map++_++fwd.hpp++>++`] +** xref:reference/header_concurrent_flat_map.adoc[`++<++boost/unordered/concurrent++_++flat++_++map.hpp++>++`] +** xref:reference/concurrent_flat_map.adoc[`concurrent_flat_map`] +** xref:reference/header_concurrent_flat_set_fwd.adoc[`++<++boost/unordered/concurrent++_++flat++_++set++_++fwd.hpp++>++`] +** xref:reference/header_concurrent_flat_set.adoc[`++<++boost/unordered/concurrent++_++flat++_++set.hpp++>++`] +** xref:reference/concurrent_flat_set.adoc[`concurrent_flat_set`] +** xref:reference/header_concurrent_node_map_fwd.adoc[`++<++boost/unordered/concurrent++_++node++_++map++_++fwd.hpp++>++`] +** xref:reference/header_concurrent_node_map.adoc[`++<++boost/unordered/concurrent++_++node++_++map.hpp++>++`] +** xref:reference/concurrent_node_map.adoc[`concurrent_node_map`] +** xref:reference/header_concurrent_node_set_fwd.adoc[`++<++boost/unordered/concurrent++_++node++_++set++_++fwd.hpp++>++`] +** xref:reference/header_concurrent_node_set.adoc[`++<++boost/unordered/concurrent++_++node++_++set.hpp++>++`] +** 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..6f511e5 --- /dev/null +++ b/doc/modules/ROOT/pages/benchmarks_zh_Hans.adoc @@ -0,0 +1,719 @@ +[#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 {plus} libstdc{plus}{plus}-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 {plus} libc{plus}{plus}, 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 {plus} 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++<++uint64++_++t, uint64++_++t++>++` +* `boost::unordered++_++flat++_++map++<++uint64++_++t, uint64++_++t++>++` +* `boost::unordered++_++node++_++map++<++uint64++_++t, uint64++_++t++>++` + +源代码可 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`^]`++<++int, int++>++` +* https://github.com/greg7mdp/gtl/blob/main/docs/phmap.md[`gtl::parallel++_++flat++_++hash++_++map`^]`++<++int, int++>++` (含64个子映射) +* `boost::concurrent++_++flat++_++map++<++int, int++>++` +* `boost::concurrent++_++node++_++map++<++int, int++>++` + +源代码可 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..9567853 --- /dev/null +++ b/doc/modules/ROOT/pages/bibliography_zh_Hans.adoc @@ -0,0 +1,12 @@ +[#bibliography] + +:idprefix: bibliography_ + += 参考文献 + +* _C/C{plus}{plus}用户期刊_,2006年2月. Pete Becker. http://www.ddj.com/cpp/184402066[STL与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..bf4f94d --- /dev/null +++ b/doc/modules/ROOT/pages/buckets_zh_Hans.adoc @@ -0,0 +1,105 @@ +[#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` +|桶的数量。 + +2+^h| *仅限闭寻址容器* +h|*方法* h|*描述* + +|`size++_++type max++_++bucket++_++count() const` +|桶数量的上限。 +|`size++_++type bucket++_++size(size++_++type n) const` +|返回桶 `n` 中的元素数量。 + +|`size++_++type bucket(key++_++type const& k) const` +|返回键 `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)` +|构造一个至少包含 `n` 个桶的空容器( `X` 表示容器类型)。 + +|`X(InputIterator i, InputIterator j, size++_++type n)` +|构造一个至少包含 `n` 个桶的空容器,并插入范围 `++[++i, j)` 中的元素( `X` 表示容器类型)。 + +|`float load++_++factor() const` +|返回每个桶的平均元素数量。 + +|`float max++_++load++_++factor() const` +|返回当前的最大负载因子。 + +|`float max++_++load++_++factor(float z)` +|以 `z` 作为提示值来更改容器的最大负载因子。 + +**开放寻址与并发容器:**此函数无效,用户不允许更改最大负载因子。 + +|`void rehash(size++_++type n)` +|调整桶的数量,使其至少为 `n` ,并确保负载因子小于最大负载因子。 + +2+^h| *仅限开放寻址与并发容器* +h|*方法* h|*描述* + +|`size++_++type max++_++load() const` +|返回容器触发重新哈希前所允许容纳的最大元素数量。 + +|=== + +关于开放寻址与并发容器的 `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..0365af5 --- /dev/null +++ b/doc/modules/ROOT/pages/changes_zh_Hans.adoc @@ -0,0 +1,322 @@ +[#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/container++_++hash/hash++_++is++_++avalanching.hpp++>++` 头文件中 `boost::hash++_++is++_++avalanching` 的 别名声明。请直接使用该头文件, `++<++boost/unordered/hash++_++traits.hpp++>++` 将在未来版本中移除。 +* 为开放寻址容器新增 `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 + +* 当头文件 `++<++memory++_++resource++>++` 可用时,新增容器 `pmr` 别名。 `boost::unordered::pmr::++[++container++]++` 别名指向使用 `std::pmr::polymorphic++_++allocator` 分配器类型的 `boost::unordered::++[++container++]++` 。 +* 为开放寻址容器与并发容器增设内部统计功能,可计算并提供受哈希函数质量影响的统计指标。通过全局宏 `BOOST++_++UNORDERED++_++ENABLE++_++STATS` 启用。 +* 雪崩哈希函数现在必须通过包含 `value` 常量且其值为 `true` 的 `is++_++avalanching` 类型别名进行标记(通常将 `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 分配器在共享内存中构建容器等应用场景。 +* 修复闭寻址容器局部迭代器的成员指针运算符缺陷(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` 与 `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` 插入元素时出现的缺陷。 + +== 版本 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 + +* 根据 http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2691.pdf[n2691^] 规范,将带提示的 `emplace` 重载更名为 `emplace++_++hint` 。 +* 在 `++<++boost/unordered/unordered++_++map++_++fwd.hpp++>++` 与 `++<++boost/unordered/unordered++_++set++_++fwd.hpp++>++` 路径下提供前向声明头文件。 +* 将所有实现移至 `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..7ee1d0b --- /dev/null +++ b/doc/modules/ROOT/pages/compliance_zh_Hans.adoc @@ -0,0 +1,99 @@ +[#compliance] += 标准符合性 + +:idprefix: compliance_ + +:cpp: C++ + +== 闭寻址容器 + +`boost::unordered++_[++multi++]++set` 和 `boost::unordered++_[++multi++]++map` 为 C{plus}{plus}11(或更高版本)编译器提供了符合最新 C{plus}{plus} 标准修订版的无序关联容器实现,仅存在文档所述的极微小偏差。这些容器完全满足 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{plus}{plus} 标准目前未规定任何开放寻址容器的实现规范,因此 `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{plus}{plus} 标准目前尚未针对此类或任何其他类型的并发数据结构制定规范。 `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..7fa6404 --- /dev/null +++ b/doc/modules/ROOT/pages/concurrent_zh_Hans.adoc @@ -0,0 +1,256 @@ +[#concurrent] += 并发容器 + +: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; + +... + +// 并行处理输入 +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] { + // 计算当前线程处理的输入范围 + 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) { + // 若因存在等价元素 y 导致插入失败 + // 可对其进行操作,例如: + ++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++] +---- +// 查找给定(唯一)值对应的键 + +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; + } + ... +} +// 处理剩余键 +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++_++map` +^|`boost::unordered++_++flat++_++map` + +|=== + +此互操作性适用于多阶段场景:部分数据处理环节需要并行执行,而其他步骤采用非并发(或只读)模式。下例中,我们需要从一个庞大的单词输入向量构建词频统计直方图:填充阶段用 `boost::concurrent++_++flat++_++map` 并行执行,随后将结果转移至最终容器。 + +[source, c++] +---- +std::vector words = ...; + +// 并行插入单词 +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; }); + }); + +// 转入常规 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..7fba285 --- /dev/null +++ b/doc/modules/ROOT/pages/debuggability_zh_Hans.adoc @@ -0,0 +1,89 @@ +[#debuggability] +:idprefix: debuggability_ + += 可调试性 + +== Visual Studio Natvis + +所有容器和迭代器在 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++_++ALL++_++NO++_++EMBEDDED++_++GDB++_++SCRIPTS` 宏,来选择在编译二进制文件时__不__嵌入美化打印器。该宏将禁用所有具备此功能的 Boost 库中内嵌的 GDB 美化打印器。 + +用户可通过外部非嵌入的 Python 脚本加载美化打印器:使用 `source` 命令添加 link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered_printers.py[/extra/boost++_++unordered++_++printers.py] 脚本即可。 + +```plaintext + +(gdb) source [/path/to/boost]/libs/unordered/extra/boost_unordered_printers.py + +``` + +=== 可视化结构 + +可视化方案与标准无序容器保持一致:映射容器显示键到映射值的关联;集合容器显示从索引到值的关联关系。迭代器可显示为指向的元素内容,或标记为结束迭代器。下图展示了示例 `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 美化打印器查看。 + +对于启用 xref:hash_quality.adoc#hash_quality_container_statistics[容器统计] 的开放寻址容器,可在 GDB 中调用 `get++_++stats()` 方法来获取统计信息。该方法在GDB中被重写为 link:https://sourceware.org/gdb/current/onlinedocs/gdb.html/Xmethod-API.html[xmethod] 实现,因此不会执行任何C{plus}{plus}同步代码。输出格式示例如下. + +```plaintext + +(gdb) print example_flat_map.get_stats() +$1 = [stats] = {[insertion] = {[count] = 5, [probe_length] = {avg = 1.0, var = 0.0, dev = 0.0}}, + [successful_lookup] = {[count] = 0, [probe_length] = {avg = 0.0, var = 0.0, dev = 0.0}, + [num_comparisons] = {avg = 0.0, var = 0.0, dev = 0.0}}, [unsuccessful_lookup] = {[count] = 5, + [probe_length] = {avg = 1.0, var = 0.0, dev = 0.0}, + [num_comparisons] = {avg = 0.0, var = 0.0, dev = 0.0}}} + +``` + +=== 花式指针 + +当用户在分配器中使用花式指针(例如 `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..6ce012d --- /dev/null +++ b/doc/modules/ROOT/pages/hash_equality_zh_Hans.adoc @@ -0,0 +1,145 @@ +[#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; + +``` + +示例目录中提供了 link:../../../examples/fnv1.hpp[FNV-1a的实现] 。 + +若需使用不同的相等性函数,则必须配套使用对应的哈希函数。例如,要实现不区分大小写的字典,就需要定义不区分大小写的相等性函数和配套的哈希函数: + +```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] 是默认的哈希函数,我们只需让自定义类型兼容 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] +|=== +|方法 |描述 + +|`hasher hash++_++function() const` +|返回容器的哈希函数。 + +|`key++_++equal key++_++eq() const` +|返回容器的键相等性函数。 + +|=== 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..d24b877 --- /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。 + +我们提供了一个 link:../../../benchmark/string_stats.cpp[示例程序] ,用于展示以下哈希函数的容器统计信息: `boost::hash++<++std::string++>++` 、 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..0381c7b --- /dev/null +++ b/doc/modules/ROOT/pages/intro_zh_Hans.adoc @@ -0,0 +1,71 @@ +[#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中的所有集合与映射均分别参照 `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; + // unordered_multiset, unordered_flat_set, unordered_node_set, + // concurrent_flat_set 和 concurrent_node_set 同理 + + template < + class Key, class Mapped, + class Hash = boost::hash, + class Pred = std::equal_to, + class Alloc = std::allocator > > + class unordered_map; + // unordered_multimap, unordered_flat_map, unordered_node_map, + // concurrent_flat_map 和 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..51b873c --- /dev/null +++ b/doc/modules/ROOT/pages/rationale_zh_Hans.adoc @@ -0,0 +1,73 @@ +[#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{plus}{plus} 标准对无序关联容器的实现规范施加了严格限制。最重要约束是隐式假设必须采用闭寻址方案。若适当放宽此规范,则能充分利用开放寻址技术优势,提供多样化的容器实现变体。 + +`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与Vigna(2021)] 。 + +当所使用的哈希函数本身已适用于开放寻址时,可通过专用的 link:../../../../container_hash/doc/html/hash.html#ref_hash_is_avalanchinghash[`hash++_++is++_++avalanching`]. + +=== 平台互操作性 + +只要不同编译器的 `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..b7ddd46 --- /dev/null +++ b/doc/modules/ROOT/pages/ref_zh_Hans.adoc @@ -0,0 +1,39 @@ +[#reference] += 参考 + +* xref:reference/header_unordered_map_fwd.adoc[`++<++boost/unordered/unordered++_++map++_++fwd.hpp++>++` 概要] +* xref:reference/header_unordered_map_top.adoc[`++<++boost/unordered++_++map.hpp++>++` 概要] +* xref:reference/header_unordered_map.adoc[`++<++boost/unordered/unordered++_++map.hpp++>++` 概要] +* xref:reference/unordered_map.adoc[类模板 `unordered++_++map`] +* xref:reference/unordered_multimap.adoc[类模板 `unordered++_++multimap`] +* xref:reference/header_unordered_set_fwd.adoc[`++<++boost/unordered/unordered++_++set++_++fwd.hpp++>++` 概要] +* xref:reference/header_unordered_set_top.adoc[`++<++boost/unordered++_++set.hpp++>++` 概要] +* xref:reference/header_unordered_set.adoc[`++<++boost/unordered/unordered++_++set.hpp++>++` 概要] +* 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[`++<++boost/unordered/unordered++_++flat++_++map++_++fwd.hpp++>++` 概要] +* xref:reference/header_unordered_flat_map.adoc[`++<++boost/unordered/unordered++_++flat++_++map.hpp++>++` 概要] +* xref:reference/unordered_flat_map.adoc[类模板 `unordered++_++flat++_++map`] +* xref:reference/header_unordered_flat_set_fwd.adoc[`++<++boost/unordered/unordered++_++flat++_++set++_++fwd.hpp++>++` 概要] +* xref:reference/header_unordered_flat_set.adoc[`++<++boost/unordered/unordered++_++flat++_++set.hpp++>++` 概要] +* xref:reference/unordered_flat_set.adoc[类模板 `unordered++_++flat++_++set`] +* xref:reference/header_unordered_node_map_fwd.adoc[`++<++boost/unordered/unordered++_++node++_++map++_++fwd.hpp++>++` 概要] +* xref:reference/header_unordered_node_map.adoc[`++<++boost/unordered/unordered++_++node++_++map.hpp++>++` 概要] +* xref:reference/unordered_node_map.adoc[类模板 `unordered++_++node++_++map`] +* xref:reference/header_unordered_node_set_fwd.adoc[`++<++boost/unordered/unordered++_++node++_++set++_++fwd.hpp++>++` 概要] +* xref:reference/header_unordered_node_set.adoc[`++<++boost/unordered/unordered++_++node++_++set.hpp++>++` 概要] +* xref:reference/unordered_node_set.adoc[类模板 `unordered++_++node++_++set`] +* xref:reference/header_concurrent_flat_map_fwd.adoc[`++<++boost/unordered/concurrent++_++flat++_++map++_++fwd.hpp++>++` 概要] +* xref:reference/header_concurrent_flat_map.adoc[`++<++boost/unordered/concurrent++_++flat++_++map.hpp++>++` 概要] +* xref:reference/concurrent_flat_map.adoc[类模板 `concurrent++_++flat++_++map`] +* xref:reference/header_concurrent_flat_set_fwd.adoc[`++<++boost/unordered/concurrent++_++flat++_++set++_++fwd.hpp++>++` 概要] +* xref:reference/header_concurrent_flat_set.adoc[`++<++boost/unordered/concurrent++_++flat++_++set.hpp++>++` 概要] +* xref:reference/concurrent_flat_set.adoc[类模板 `concurrent++_++flat++_++set`] +* xref:reference/header_concurrent_node_map_fwd.adoc[`++<++boost/unordered/concurrent++_++node++_++map++_++fwd.hpp++>++` 概要] +* xref:reference/header_concurrent_node_map.adoc[`++<++boost/unordered/concurrent++_++node++_++map.hpp++>++` 概要] +* xref:reference/concurrent_node_map.adoc[类模板 `concurrent++_++node++_++map`] +* xref:reference/header_concurrent_node_set_fwd.adoc[`++<++boost/unordered/concurrent++_++node++_++set++_++fwd.hpp++>++` 概要] +* xref:reference/header_concurrent_node_set.adoc[`++<++boost/unordered/concurrent++_++node++_++set.hpp++>++` 概要] +* xref:reference/concurrent_node_set.adoc[类模板 `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..8055207 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/concurrent_flat_map_zh_Hans.adoc @@ -0,0 +1,1844 @@ +[#concurrent_flat_map] +== 类模板 concurrent++_++flat++_++map + +:idprefix: concurrent_flat_map_ + +`boost::concurrent++_++flat++_++map` — 一种哈希表,用于将唯一键与另一个值相关联,并支持在无外部同步机制的情况下并发执行元素的插入、擦除、查找及访问操作。 + +尽管 `boost::concurrent++_++flat++_++map` 充当容器的角色,但它并不符合标准C{plus}{plus}的 https://en.cppreference.com/w/cpp/named_req/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: + // 类型 + 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] + + // 常量 + static constexpr size_type xref:#concurrent_flat_map_constants[bulk_visit_size] = _implementation-defined_; + + // 构造/复制/销毁 + 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; + + + // 访问 + 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; + + // 容量 + ++[[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; + + // 修改器 + 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); + + // 观察器 + hasher xref:#concurrent_flat_map_hash_function[hash_function]() const; + key_equal xref:#concurrent_flat_map_key_eq[key_eq]() const; + + // 映射操作 + 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; + + // 桶接口 + size_type xref:#concurrent_flat_map_bucket_count[bucket_count]() const noexcept; + + // 哈希策略 + 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); + + // 统计(如果启用) + stats xref:#concurrent_flat_map_get_stats[get_stats]() const; + void xref:#concurrent_flat_map_reset_stats[reset_stats]() noexcept; + }; + + // 推导指南 + 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>; + +} // 命名空间 unordered +} // 命名空间 boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_键_ +.2+|`Key` 和 `T` 必须满足 https://en.cppreference.com/w/cpp/named_req/MoveConstructible[可移动构造] 要求。 `std::pair++<++const Key, T++>++` 必须能够从任何可转换的 `std::pair` 对象 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原位构造] 到容器中,并且必须能够从容器中 https://en.cppreference.com/w/cpp/named_req/Erasable[可擦除] 。 + +|_T_ + +|_Hash_ +|一元函数对象类型,用作 `Key` 的哈希函数。它接受一个类型为 `Key` +的参数,并返回一个 `std::size++_++t` 类型的值。 + +|_谓词_ +|二元函数对象,用于在 `Key` +类型的值上建立等价关系。它接受两个类型为 `Key` 的参数,并返回一个 `bool` +类型的值。 + +|_分配器_ +|一种分配器,其值类型与容器的值类型相同。支持使用 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` 上并发调用以下操作时不得引入数据竞争: + +* 从 `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++_++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` 操作,都会与针对 `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.reset++_++stats()` 。 + +[horizontal] +并发性;; 阻塞于 `other` 。 + +--- + +==== 从 unordered++_++flat++_++map 的移动构造函数 + +```c++ + +concurrent_flat_map(unordered_flat_map&& other); + +``` + +从 xref:#unordered_flat_map[`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); + +``` + +构造一个空容器,使用 `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++ + +~concurrent_flat_map(); + +``` + +[horizontal] +注意;; 析构函数会作用于每个元素,并释放所有内存 + +--- + +=== 赋值操作 + +==== 复制赋值 + +```c++ + +concurrent_flat_map& operator=(concurrent_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[可复制插入] 要求 +并发性;; 阻塞于 `++*++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` 存在且 +`Alloc::propagate++_++on++_++container++_++move++_++assignment::value` +为 `true` ,则从 `other` 移动赋值分配器。若此时分配器与 +`other.get++_++allocator()` 相等,则直接将 `other` 的内部桶数组转移至 +`++*++this` ;否则,将插入 `other` +中元素的移动构造副本。如果统计功能已 xref:#concurrent_flat_map_boost_unordered_enable_stats[启用] ,则当且仅当最终分配器与 +`other.get++_++allocator()` 相等时,同时转移其内部统计信息,并始终调用 +`other.reset++_++stats()` 。 + +[horizontal] +并发性;; 阻塞于 `++*++this` 和 `other` 。 + +--- + +==== 初始化列表赋值 +```c++ + +concurrent_flat_map& operator=(std::initializer_list il); + +``` + +从初始化列表中的值赋值。该操作销毁所有原有元素。 + +[horizontal] +要求;; `value++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入] 要求 +并发性;; 阻塞于 `++*++this` 。 + +--- + +=== 访问操作 + +==== ++[++c++]++visit( ++[++c++]++ 访问) + +```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++<++class K, class F++>++` 重载才会参与重载决议。标准库假定 `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` 为常量时,该引用也为 常量引用。 + +尽管在功能上等同于为每个键单独调用 xref:#concurrent_flat_map_cvisit[`++[++c++]++visit`] ,但由于内部流线化优化,批量访问通常表现更优。建议使 `std::distance(first,last)` 至少达到 xref:#concurrent_flat_map_constants[`bulk++_++visit++_++size`] 以获得性能提升:超过此大小,性能预计不会进一步提升。 + +[horizontal] +要求;; `FwdIterator` 需满足 https://en.cppreference.com/w/cpp/named_req/ForwardIterator[LegacyForwardIterator] 要求(C{plus}{plus}11 至 C{plus}{plus}17)或符合 https://en.cppreference.com/w/cpp/iterator/forward_iterator[std::forward++_++iterator] 规范(C{plus}{plus}20 及更高版本)。对于 `K` = `std::iterator++_++traits++<++FwdIterator++>++::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{plus}{plus}17 并行算法的编译器中可用。 + ++ +仅当 `std::is++_++execution++_++policy++_++v++<++std::remove++_++cvref++_++t++<++ExecutionPolicy++>>++` 为 `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{plus}{plus}17 并行算法的编译器中可用。 + ++ +仅当 `std::is++_++execution++_++policy++_++v++<++std::remove++_++cvref++_++t++<++ExecutionPolicy++>>++` 为 `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] +返回;; 返回该容器可能包含的最大元素数量 `size()` 。 + +--- + +=== 修改器 + +==== 原地构造 +```c++ + +template bool emplace(Args&&... args); + +``` + +当且仅当容器中不存在具有等效键的元素时,插入一个由参数 `args` 构造的对象。 + +[horizontal] +要求;; `value++_++type` 必须能够从 `args` 参数构造。 +返回;; 若发生插入,则返回 `true` 。 +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 若触发重哈希操作,则使指向元素的指针和引用失效。 + ++ +若 `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` 的重哈希。 +注意;; 若触发重哈希操作,则使指向元素的指针和引用失效。 + ++ +对于 `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` 的重哈希。 +注意;; 若触发重哈希操作,则使指向元素的指针和引用失效。 + ++ +对于 `insert(x)` 形式的调用(其中 `x` 可同等地转换为 `const value++_++type&` 和 `const 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` 的重哈希。 +注意;; 若触发重哈希操作,则使指向元素的指针和引用失效。 + ++ +此接口仅为说明,因 C{plus}{plus} 不允许在可变参数包后声明参数 `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[可复制插入] 要求。 +返回;; 若发生插入,则返回 `true` 。 + +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 若触发重哈希操作,则使指向元素的指针和引用失效。 + ++ +在 `insert++_++or++_[++c++]++visit(obj, f)` 形式的调用中,仅当 `std::remove++_++cv++<++std::remove++_++reference++<++decltype(obj)++>++::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[可移动插入] 要求。 +返回;; 若发生插入,则返回 `true` 。 + +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 若触发重哈希操作,则使指向元素的指针和引用失效。 + ++ +在 `insert++_++or++_[++c++]++visit(obj, f)` 形式的调用中,仅当 `std::remove++_++reference++<++decltype(obj)++>++::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` 的重哈希。 +注意;; 若触发重哈希操作,则使指向元素的指针和引用失效。 + ++ +此接口仅为说明,因为 C{plus}{plus} 不允许在可变参数包后声明参数 `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` 的重哈希。 +注意;; 若触发重哈希操作,则使指向元素的指针和引用失效。 + ++ +在 `insert++_++and++_[++c++]++visit(obj, f1, f2)` 形式的调用中,仅当 `std::remove++_++cv++<++std::remove++_++reference++<++decltype(obj)++>++::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[可移动插入] 要求。 +返回;; 若发生插入,则返回 `true` 。 + +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 若触发重哈希操作,则使指向元素的指针和引用失效。 + ++ +在 `insert++_++and++_[++c++]++visit(obj, f1, f2)` 形式的调用中,仅当 `std::remove++_++reference++<++decltype(obj)++>++::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 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` 对象;否则,将按以下形式构造: + ++ +-- +```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:#concurrent_flat_map_emplace[emplace] 不同,后者只是将所有参数转发给 `value++_++type` 的构造函数。 + +若触发重哈希操作,则使指向元素的指针和引用失效。 + +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类别名义时, `template++<++class K, class... Args++>++` 重载会才参与重决议析。标准库假设 `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` 对象;否则,将按以下形式构造: + ++ +-- +```c++ + +// 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{plus}{plus} 不允许在可变参数包后声明参数 `f` 。 + +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K, class... Args, class F++>++` 重载才会参与重载决议。标准库假定 `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` 对象;否则,将按以下形式构造: + ++ +-- +```c++ + +// 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{plus}{plus} 不允许在可变参数包后声明参数 `f1` 和 `f2` 。 + +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K, class... Args, class F1, class F2++>++` 重载才会参与重载决议。标准库假定 `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++<++M++>++(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] +返回;; 若发生插入,则返回 `true` 。 +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 若触发重哈希操作,则使指向元素的指针和引用失效。 + ++ +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K, class M++>++` 才会参与重载决议。标准库假设 `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++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 抛出异常时,本函数才会抛出异常。 +注意;; 向 `f` 传递指向 `x` 的非常量引用。 + ++ +仅当 `std::is++_++execution++_++policy++_++v++<++std::remove++_++cvref++_++t++<++ExecutionPolicy++>>++` 为 `false` 时, `template++<++class K, class F++>++` 重载才会参与重载决议。 + ++ +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K, class F++>++` 重载才会参与重载决议。标准库假定 `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{plus}{plus}17 并行算法的编译器中可用。 + ++ +仅当 `std::is++_++execution++_++policy++_++v++<++std::remove++_++cvref++_++t++<++ExecutionPolicy++>>++` 为 `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` 已声明, 且其 `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_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++<++class K++>++` 重载才会参与重载决议。标准库假设 `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++<++class K++>++` 重载才会参与重载决议。标准库假设 `Hash` 可同时被 `K` 与 `Key` 类型调用,且 `Pred` 为透明的。这支持异构查找,该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + ++ +在存在并发插入操作的情况下,返回值可能无法准确反映函数执行完成后容器的真实状态。 + +--- +=== 桶接口 + +==== bucket++_++count +```c++ + +size_type bucket_count() const noexcept; + +``` + +[horizontal] +返回;; 桶数组的大小。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ + +float load_factor() const noexcept; + +``` + +[horizontal] +返回;; 返回 `static++_++cast++<++float++>++(size())/static++_++cast++<++float++>++(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] +返回;; 返回容器截至目前所执行的插入与查找操作的统计信息。 +注意;; 仅当 xref:reference/stats.adoc#stats[统计计算] 功能被 xref:#concurrent_flat_map_boost_unordered_enable_stats[启用] 时可用。 + +--- + +==== reset++_++stats +```c++ + +void reset_stats() noexcept; + +``` + +[horizontal] +效果;; 将容器所保存的内部统计信息重置为零。 +注意;; 仅当 xref:reference/stats.adoc#stats[统计计算] 功能被 xref:#concurrent_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:#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++<++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++_++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..823776e --- /dev/null +++ b/doc/modules/ROOT/pages/reference/concurrent_flat_set_zh_Hans.adoc @@ -0,0 +1,1640 @@ +[#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: + // 类型 + 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] + + // 常量 + static constexpr size_type xref:#concurrent_flat_set_constants[bulk_visit_size] = _implementation-defined_; + + // 构造/复制/销毁 + 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; + + + // 访问 + 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; + + // 容量 + ++[[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; + + // 修改器 + 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); + + // 观察器 + hasher xref:#concurrent_flat_set_hash_function[hash_function]() const; + key_equal xref:#concurrent_flat_set_key_eq[key_eq]() const; + + // 集合操作 + 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; + + // 桶接口 + size_type xref:#concurrent_flat_set_bucket_count[bucket_count]() const noexcept; + + // 哈希策略 + 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); + + // 统计(若启用) + stats xref:#concurrent_flat_set_get_stats[get_stats]() const; + void xref:#concurrent_flat_set_reset_stats[reset_stats]() noexcept; + }; + + // 推导指引 + 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>; + +} // 命名空间 unordered +} // 命名空间 boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_键_ +|`Key` 必须满足 +https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入] +到容器中的要求,且需满足从容器中 +https://en.cppreference.com/w/cpp/named_req/Erasable[可擦除] 的要求。 + +|_Hash_ +|一元函数对象类型,用作 `Key` 的哈希函数。它接受一个类型为 `Key` +的参数,并返回一个 `std::size++_++t` 类型的值。 + +|_谓词_ +|二元函数对象,用于在 `Key` +类型的值上建立等价关系。它接受两个类型为 `Key` 的参数,并返回一个 `bool` +类型的值。 + +|_分配器_ +|一种分配器,其值类型与容器的值类型相同。 `std::allocator++_++traits++<++Allocator++>++::pointer` +和 `std::allocator++_++traits++<++Allocator++>++::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`]`++<++Hash++>++::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++_++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[启用] ,当且仅当 `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`][`unordered++_++flat++_++set`] 进行移动构造。 `other` 的内部桶数组直接转移至新容器。其哈希函数、相等性谓词和分配器从 `other` 移动构造。若统计功能 xref:#concurrent_flat_set_boost_unordered_enable_stats[启用] ,则转移 `other` 的内部统计信息,并调用 `other.reset++_++stats()` 。 + +[horizontal] +复杂度;; 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); + +``` + +构造一个空容器,使用 `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` 存在,且 `Alloc::propagate++_++on++_++container++_++copy++_++assignment::value` 为 `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` 存在且 +`Alloc::propagate++_++on++_++container++_++move++_++assignment::value` +为 `true` ,则从 `other` 移动赋值分配器。若此时分配器与 +`other.get++_++allocator()` 相等,则将 `other` 的内部桶数组直接转移至 +`++*++this` ;否则插入 `other` +元素的移动构造副本。若统计功能 xref:#concurrent_flat_set_boost_unordered_enable_stats[启用] ,当且仅当最终分配器与 +`other.get++_++allocator()` 相等时,转移内部统计信息,并始终调用 +`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[可复制插入] 要求 +并发性;; 阻塞于 `++*++this` 。 + +--- + +=== 访问操作 + +==== ++[++c++]++visit( ++[++c++]++ 访问) + +```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++<++class K, class F++>++` 重载才会参与重载决议。标准库假定 `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` 。 + +尽管在功能上等同于对每个键单独调用 xref:#concurrent_flat_set_cvisit[`++[++c++]++visit`] ,但批量访问因内部流线优化而通常具有更快的执行速度。建议 `std::distance(first,last)` 至少达到 xref:#concurrent_flat_set_constants[`bulk++_++visit++_++size`] 以获得性能提升:超过此大小后,性能预计不再提升。 + +[horizontal] +要求;; `FwdIterator` 需满足 https://en.cppreference.com/w/cpp/named_req/ForwardIterator[LegacyForwardIterator] 要求(C{plus}{plus}11 至 C{plus}{plus}17)或符合 https://en.cppreference.com/w/cpp/iterator/forward_iterator[std::forward++_++iterator] 规范(C{plus}{plus}20 及更高版本)。对于 `K` = `std::iterator++_++traits++<++FwdIterator++>++::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{plus}{plus}17 并行算法的编译器中可用。 + ++ +仅当 `std::is++_++execution++_++policy++_++v++<++std::remove++_++cvref++_++t++<++ExecutionPolicy++>>++` 为 `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{plus}{plus}17 并行算法的编译器中可用。 + ++ +仅当 `std::is++_++execution++_++policy++_++v++<++std::remove++_++cvref++_++t++<++ExecutionPolicy++>>++` 为 `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] +返回;; 返回该容器可能包含的最大元素数量 `size()` 。 + +--- + +=== 修改器 + +==== 原地构造 +```c++ + +template bool emplace(Args&&... args); + +``` + +当且仅当容器中不存在具有等效键的元素时,插入一个由参数 `args` 构造的对象。 + +[horizontal] +要求;; `value++_++type` 必须能够从 `args` 参数构造。 +返回;; 若发生插入,则返回 `true` 。 +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 若触发重哈希操作,则使指向元素的指针和引用失效。 + +--- + +==== 复制插入 +```c++ + +bool insert(const value_type& obj); + +``` + +当且仅当容器中不存在等价键的元素时,将 `obj` 对象插入到容器。 + +[horizontal] +要求;; `value++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入] 要求。 +返回;; 若发生插入,则返回 `true` 。 + +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 若触发重哈希操作,则使指向元素的指针和引用失效。 + +--- + +==== 移动插入 +```c++ + +bool insert(value_type&& obj); + +``` + +当且仅当容器中不存在等价键的元素时,将 `obj` 对象插入到容器。 + +[horizontal] +要求;; `value++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入] 要求。 +返回;; 若发生插入,则返回 `true` 。 +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 若触发重哈希操作,则使指向元素的指针和引用失效。 + +--- + +==== 透明插入 +```c++ + +template bool insert(K&& k); + +``` + +当且仅当容器中不存在等价键的元素时,插入一个由 `std::forward++<++K++>++(k)` 构造的元素。 + +[horizontal] +要求;; `value++_++type` 需满足从 `k` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] 的要求。 +返回;; 若发生插入,则返回 `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{plus}{plus} 不允许在可变参数包后声明参数 `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` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入] 要求。 +返回;; 若发生插入,则返回 `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` 需满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入] 要求。 +返回;; 若发生插入,则返回 `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++>++(k)` 构造的元素;否则,使用指向等效元素的常量引用调用 `f` 。 + +[horizontal] +要求;; `value++_++type` 需满足从 `k` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] 的要求。 +返回;; 若发生插入,则返回 `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{plus}{plus} 不允许在可变参数包后声明参数 `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` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入] 要求。 +返回;; 若发生插入,则返回 `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` 需满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入] 要求。 +返回;; 若发生插入,则返回 `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++>++(k)` 构造的元素,并使用指向新创建元素的常量引用调用 `f1` ;否则,使用指向等效元素的常量引用调用 `f2` 。 + +[horizontal] +要求;; `value++_++type` 需满足从 `k` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] 的要求。 +返回;; 若发生插入,则返回 `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` 抛出异常时,才会抛出异常。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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++<++std::remove++_++cvref++_++t++<++ExecutionPolicy++>>++` 为 `false` 时, `template++<++class K, class F++>++` 重载才会参与重载决议。 + ++ +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K, class F++>++` 重载才会参与重载决议。标准库假定 `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{plus}{plus}17 并行算法的编译器中可用。 + ++ +仅当 `std::is++_++execution++_++policy++_++v++<++std::remove++_++cvref++_++t++<++ExecutionPolicy++>>++` 为 `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` 已声明, 且其 `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)。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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++<++class K++>++` 重载才会参与重载决议。标准库假设 `Hash` 可同时被 `K` 与 `Key` 类型调用,且 `Pred` 为透明的。这支持异构查找,该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + ++ +在存在并发插入操作的情况下,返回值可能无法准确反映函数执行完成后容器的真实状态。 + +--- +=== 桶接口 + +==== bucket++_++count +```c++ + +size_type bucket_count() const noexcept; + +``` + +[horizontal] +返回;; 桶数组的大小。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ + +float load_factor() const noexcept; + +``` + +[horizontal] +返回;; 返回 `static++_++cast++<++float++>++(size())/static++_++cast++<++float++>++(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..bb5d036 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/concurrent_node_map_zh_Hans.adoc @@ -0,0 +1,1963 @@ +[#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 + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + class concurrent_node_map { + public: + // 类型 + 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] + + // 常量 + static constexpr size_type xref:#concurrent_node_map_constants[bulk_visit_size] = _implementation-defined_; + + // 构造/复制/销毁 + 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; + + + // 访问 + 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; + + // 容量 + ++[[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; + + // 修改器 + 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); + + // 观察器 + hasher xref:#concurrent_node_map_hash_function[hash_function]() const; + key_equal xref:#concurrent_node_map_key_eq[key_eq]() const; + + // 映射操作 + 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; + + // 桶接口 + size_type xref:#concurrent_node_map_bucket_count[bucket_count]() const noexcept; + + // 哈希策略 + 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); + + // 统计(若启用) + stats xref:#concurrent_node_map_get_stats[get_stats]() const; + void xref:#concurrent_node_map_reset_stats[reset_stats]() noexcept; + }; + + // 推导指引 + 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>; + +} // 命名空间 unordered +} // 命名空间 boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_键_ +.2+|`std::pair++<++const Key, T++>++` 必须能够从任何可转换为该类型的 +`std::pair` +对象 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] 到容器中,并且必须支持从容器中 https://en.cppreference.com/w/cpp/named_req/Erasable[擦除] 。 + +|_T_ + +|_Hash_ +|一元函数对象类型,用作 `Key` 的哈希函数。它接受一个类型为 `Key` +的参数,并返回一个 `std::size++_++t` 类型的值。 + +|_谓词_ +|二元函数对象,用于在 `Key` +类型的值上建立等价关系。它接受两个类型为 `Key` 的参数,并返回一个 `bool` +类型的值。 + +|_分配器_ +|一种分配器,其值类型与容器的值类型相同。支持使用 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` 上并发调用以下操作时不得引入数据竞争: + +* 从 `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++_++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`][`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] +要求;; `hasher` 和 `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[可默认构造] 要求。 + +--- + +==== 带桶数和分配器的初始化列表构造函数 + +```c++ + +concurrent_node_map(std::initializer_list il, size_type n, const allocator_type& a); + +``` + +构造一个空容器,使用 `a` 作为分配器以及默认的哈希函数和键相等性谓词,并将 `il` 中的元素插入其中。 + +[horizontal] +要求;; `hasher` 和 `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/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` 存在,且 `Alloc::propagate++_++on++_++container++_++copy++_++assignment::value` 为 `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` 存在且其 +`Alloc::propagate++_++on++_++container++_++move++_++assignment::value` +为 `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++]++ 访问) + +```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++<++class K, class F++>++` 重载才会参与重载决议。标准库假定 `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` 为常量时,该引用也为 常量引用。 + +尽管在功能上等效于对每个键单独调用 xref:#concurrent_node_map_cvisit[`++[++c++]++visit`] ,但批量访问因内部流线型优化而通常具有更快的执行速度。建议 `std::distance(first,last)` 至少为 xref:#concurrent_node_map_constants[`bulk++_++visit++_++size`] 以获得性能提升:超过此大小,性能预计不会进一步提升。 + +[horizontal] +要求;; `FwdIterator` 需满足 https://en.cppreference.com/w/cpp/named_req/ForwardIterator[LegacyForwardIterator] 要求(C{plus}{plus}11 至 C{plus}{plus}17)或符合 https://en.cppreference.com/w/cpp/iterator/forward_iterator[std::forward++_++iterator] 规范(C{plus}{plus}20 及更高版本)。对于 `K` = `std::iterator++_++traits++<++FwdIterator++>++::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{plus}{plus}17 并行算法的编译器中可用。 + ++ +仅当 `std::is++_++execution++_++policy++_++v++<++std::remove++_++cvref++_++t++<++ExecutionPolicy++>>++` 为 `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{plus}{plus}17 并行算法的编译器中可用。 + ++ +仅当 `std::is++_++execution++_++policy++_++v++<++std::remove++_++cvref++_++t++<++ExecutionPolicy++>>++` 为 `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] +返回;; 返回该容器可能包含的最大元素数量 `size()` 。 + +--- + +=== 修改器 + +==== 原地构造 +```c++ + +template bool emplace(Args&&... args); + +``` + +当且仅当容器中不存在具有等效键的元素时,插入一个由参数 `args` 构造的对象。 + +[horizontal] +要求;; `value++_++type` 必须能够从 `args` 参数构造。 +返回;; 若发生插入,则返回 `true` 。 +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 若 `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` 的重哈希。 +注意;; 对于 `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` 的重哈希。 +注意;; 对于 `insert(x)` 形式的调用(其中 `x` 可同等地转换为 `const value++_++type&` 和 `const 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` 的原值。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 如果 `nh` 非空,且 `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` 。 +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 此接口仅为说明,因 C{plus}{plus} 不允许在可变参数包后声明参数 `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[可复制插入] 要求。 +返回;; 若发生插入,则返回 `true` 。 + +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 在 `insert++_++or++_[++c++]++visit(obj, f)` 形式的调用中,仅当 `std::remove++_++cv++<++std::remove++_++reference++<++decltype(obj)++>++::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[可移动插入] 要求。 +返回;; 若发生插入,则返回 `true` 。 + +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 在 `insert++_++or++_[++c++]++visit(obj, f)` 形式的调用中,仅当 `std::remove++_++reference++<++decltype(obj)++>++::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` 的原值。 +抛出;; 若异常由调用 `hasher` 或调用 `f` 以外的操作抛出,则此函数不产生任何效果。 +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 如果 `nh` 非空,且 `nh` 与容器的分配器不相等,则行为未定义。 + +--- + +==== 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{plus}{plus} 不允许在可变参数包后声明参数 `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` 的重哈希。 +注意;; 在 `insert++_++and++_[++c++]++visit(obj, f1, f2)` 形式的调用中,仅当 `std::remove++_++cv++<++std::remove++_++reference++<++decltype(obj)++>++::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[可移动插入] 要求。 +返回;; 若发生插入,则返回 `true` 。 + +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 在 `insert++_++and++_[++c++]++visit(obj, f1, f2)` 形式的调用中,仅当 `std::remove++_++reference++<++decltype(obj)++>++::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` (此处应为 `insert++_++and++_++cvisit` ,可能是英文出了点错误)时该引用为常量引用。 + +[horizontal] +返回;; 返回一个基于 `inserted` 和 `node` 构造的 `insert++_++return++_++type` 对象: + +* 若 `nh` 为空,则 `inserted` 为 `false` 且 `node` 为空。 +* 若插入操作成功,则 `inserted` 为 true, 且 `node` 为空。 +* 若插入操作失败,则 `inserted` 为 false ,且 `node` 保留 `nh` 的原值。 +抛出;; 若异常由调用 `hasher` 、 `f1` 或 `f2` 以外的操作抛出,则此函数不产生任何效果。 +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 如果 `nh` 非空,且 `nh` 与容器的分配器不相等,则行为未定义。 + +--- + +==== 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` ;否则,将按以下形式执行构造: + ++ +-- +```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:#concurrent_node_map_emplace[emplace] 不同——该操作仅将所有参数直接转发给 `value++_++type` 的构造函数。 + +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类别名义时, `template++<++class K, class... Args++>++` 重载会才参与重决议析。标准库假设 `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` 对象;否则,将按以下形式构造: + ++ +-- +```c++ + +// 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{plus}{plus} 不允许在可变参数包后声明参数 `f` 。 + +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K, class... Args, class F++>++` 重载才会参与重载决议。标准库假定 `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` 对象;否则,将按以下形式构造: + ++ +-- +```c++ + +// 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{plus}{plus} 不允许在可变参数包后声明参数 `f1` 和 `f2` 。 + +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K, class... Args, class F1, class F2++>++` 重载才会参与重载决议。标准库假定 `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++<++M++>++(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] +返回;; 若发生插入,则返回 `true` 。 +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K, class M++>++` 才会参与重载决议。标准库假设 `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++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 抛出异常时,本函数才会抛出异常。 +注意;; 向 `f` 传递指向 `x` 的非常量引用。 + ++ +仅当 `std::is++_++execution++_++policy++_++v++<++std::remove++_++cvref++_++t++<++ExecutionPolicy++>>++` 为 `false` 时, `template++<++class K, class F++>++` 重载才会参与重载决议。 + ++ +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K, class F++>++` 重载才会参与重载决议。标准库假定 `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{plus}{plus}17 并行算法的编译器中可用。 + ++ +仅当 `std::is++_++execution++_++policy++_++v++<++std::remove++_++cvref++_++t++<++ExecutionPolicy++>>++` 为 `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` 已声明, 且其 `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++<++class K++>++` 重载才会参与重载决议。标准库假设 `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++<++class K++>++` 重载才会参与重载决议。标准库假设 `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++<++class K++>++` 重载才会参与重载决议。标准库假设 `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++<++class K++>++` 重载才会参与重载决议。标准库假设 `Hash` 可同时被 `K` 与 `Key` 类型调用,且 `Pred` 为透明的。这支持异构查找,该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + ++ +在存在并发插入操作的情况下,返回值可能无法准确反映函数执行完成后容器的真实状态。 + +--- +=== 桶接口 + +==== bucket++_++count +```c++ + +size_type bucket_count() const noexcept; + +``` + +[horizontal] +返回;; 桶数组的大小。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ + +float load_factor() const noexcept; + +``` + +[horizontal] +返回;; 返回 `static++_++cast++<++float++>++(size())/static++_++cast++<++float++>++(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] +返回;; 返回容器截至目前所执行的插入与查找操作的统计信息。 +注意;; 仅在 xref:reference/stats.adoc#stats[统计计算] 功能 xref:#concurrent_node_map_boost_unordered_enable_stats[启用] 时可用。 + +--- + +==== reset++_++stats +```c++ + +void reset_stats() noexcept; + +``` + +[horizontal] +效果;; 将容器所保存的内部统计信息重置为零。 +注意;; 仅在 xref:reference/stats.adoc#stats[统计计算] 功能 xref:#concurrent_node_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:#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..a7ab335 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/concurrent_node_set_zh_Hans.adoc @@ -0,0 +1,1763 @@ +[#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: + // 类型 + 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] + + // 常量 + static constexpr size_type xref:#concurrent_node_set_constants[bulk_visit_size] = _implementation-defined_; + + // 构造/复制/销毁 + 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; + + + // 访问 + 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; + + // 容量 + ++[[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; + + // 修改器 + 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); + + // 观察器 + hasher xref:#concurrent_node_set_hash_function[hash_function]() const; + key_equal xref:#concurrent_node_set_key_eq[key_eq]() const; + + // 集合操作 + 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; + + // 桶接口 + size_type xref:#concurrent_node_set_bucket_count[bucket_count]() const noexcept; + + // 哈希策略 + 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); + + // 统计(若已启用) + stats xref:#concurrent_node_set_get_stats[get_stats]() const; + void xref:#concurrent_node_set_reset_stats[reset_stats]() noexcept; + }; + + // 推导指引 + 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>; + +} // 命名空间 unordered +} // 命名空间 boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_键_ +|`Key` 必须满足 +https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入] +到容器中的要求,且需满足从容器中 +https://en.cppreference.com/w/cpp/named_req/Erasable[可擦除] 的要求。 + +|_Hash_ +|一元函数对象类型,用作 `Key` 的哈希函数。它接受一个类型为 `Key` +的参数,并返回一个 `std::size++_++t` 类型的值。 + +|_谓词_ +|二元函数对象,用于在 `Key` +类型的值上建立等价关系。它接受两个类型为 `Key` 的参数,并返回一个 `bool` +类型的值。 + +|_分配器_ +|一种分配器,其值类型与容器的值类型相同。 `std::allocator++_++traits++<++Allocator++>++::pointer` +和 `std::allocator++_++traits++<++Allocator++>++::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`]`++<++Hash++>++::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[可默认构造] 要求。 + +--- + +==== 桶数构造函数 +```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[可默认构造] 要求。 + +--- + +==== 迭代器范围构造函数 +[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` 移动构造。若统计功能以 xref:#concurrent_node_set_boost_unordered_enable_stats[启用] ,则同时转移 `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[可默认构造] 要求。 + +--- + +==== 分配器构造函数 +```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` 复制构造。若统计功能已 xref:#concurrent_node_set_boost_unordered_enable_stats[启用] ,则当且仅当 `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`][`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[可默认构造] 要求。 + +--- + +==== 带哈希函数和分配器的桶数构造函数 +```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[可默认构造] 要求。 + +--- + +==== 带桶数和分配器的迭代器范围构造函数 +[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[可默认构造] 要求。 + +--- + +==== 带桶数和哈希函数的迭代器范围构造函数 +[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[可默认构造] 要求。 + +--- + +==== 带桶数和分配器的初始化列表构造函数 + +```c++ + +concurrent_node_set(std::initializer_list il, size_type n, const allocator_type& a); + +``` + +构造一个空容器,使用 `a` 作为分配器以及默认的哈希函数和键相等性谓词,并将 `il` 中的元素插入其中。 + +[horizontal] +要求;; `hasher` 和 `key++_++equal` 需满足 https://en.cppreference.com/w/cpp/named_req/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[可复制插入] 要求 +并发性;; 阻塞于 `++*++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.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[可复制插入] 要求 +并发性;; 阻塞于 `++*++this` 。 + +--- + +=== 访问操作 + +==== ++[++c++]++visit( ++[++c++]++ 访问) + +```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++<++class K, class F++>++` 重载才会参与重载决议。标准库假定 `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` 。 + +尽管批量访问在功能上等效于对每个键单独调用 xref:#concurrent_node_set_cvisit[`++[++c++]++visit`] ,但该操作因内部流线化优化而通常表现更优。建议 `std::distance(first,last)` 至少达到 xref:#concurrent_node_set_constants[`bulk++_++visit++_++size`] 以获得性能提升:超过此规模后,性能预计不会进一步增长。 + +[horizontal] +要求;; `FwdIterator` 需满足 https://en.cppreference.com/w/cpp/named_req/ForwardIterator[LegacyForwardIterator] 要求(C{plus}{plus}11 至 C{plus}{plus}17)或符合 https://en.cppreference.com/w/cpp/iterator/forward_iterator[std::forward++_++iterator] 规范(C{plus}{plus}20 及更高版本)。对于 `K` = `std::iterator++_++traits++<++FwdIterator++>++::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{plus}{plus}17 并行算法的编译器中可用。 + ++ +仅当 `std::is++_++execution++_++policy++_++v++<++std::remove++_++cvref++_++t++<++ExecutionPolicy++>>++` 为 `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{plus}{plus}17 并行算法的编译器中可用。 + ++ +仅当 `std::is++_++execution++_++policy++_++v++<++std::remove++_++cvref++_++t++<++ExecutionPolicy++>>++` 为 `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] +返回;; 返回该容器可能包含的最大元素数量 `size()` 。 + +--- + +=== 修改器 + +==== 原地构造 +```c++ + +template bool emplace(Args&&... args); + +``` + +当且仅当容器中不存在具有等效键的元素时,插入一个由参数 `args` 构造的对象。 + +[horizontal] +要求;; `value++_++type` 必须能够从 `args` 参数构造。 +返回;; 若发生插入,则返回 `true` 。 +并发性;; 阻塞于 `++*++this` 的重哈希。 + +--- + +==== 复制插入 +```c++ + +bool insert(const value_type& obj); + +``` + +当且仅当容器中不存在等价键的元素时,将 `obj` 对象插入到容器。 + +[horizontal] +要求;; `value++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入] 要求。 +返回;; 若发生插入,则返回 `true` 。 + +并发性;; 阻塞于 `++*++this` 的重哈希。 + +--- + +==== 移动插入 +```c++ + +bool insert(value_type&& obj); + +``` + +当且仅当容器中不存在等价键的元素时,将 `obj` 对象插入到容器。 + +[horizontal] +要求;; `value++_++type` 需满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入] 要求。 +返回;; 若发生插入,则返回 `true` 。 +并发性;; 阻塞于 `++*++this` 的重哈希。 + +--- + +==== 透明插入 +```c++ + +template bool insert(K&& k); + +``` + +当且仅当容器中不存在等价键的元素时,插入一个由 `std::forward++<++K++>++(k)` 构造的元素。 + +[horizontal] +要求;; `value++_++type` 需满足从 `k` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] 的要求。 +返回;; 若发生插入,则返回 `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` 的原值。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 如果 `nh` 非空,且 `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{plus}{plus} 不允许在可变参数包后声明参数 `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` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入] 要求。 +返回;; 若发生插入,则返回 `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` 需满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入] 要求。 +返回;; 若发生插入,则返回 `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++>++(k)` 构造的元素;否则,使用指向等效元素的常量引用调用 `f` 。 + +[horizontal] +要求;; `value++_++type` 需满足从 `k` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] 的要求。 +返回;; 若发生插入,则返回 `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` 的原值。 +抛出;; 若异常由调用 `hasher` 或调用 `f` 以外的操作抛出,则此函数不产生任何效果。 +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 如果 `nh` 非空,且 `nh` 与容器的分配器不相等,则行为未定义。 + +--- + +==== 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` 。 +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 此接口仅为说明,因为 C{plus}{plus} 不允许在可变参数包后声明参数 `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` 需满足 https://en.cppreference.com/w/cpp/named_req/CopyInsertable[可复制插入] 要求。 +返回;; 若发生插入,则返回 `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` 需满足 https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入] 要求。 +返回;; 若发生插入,则返回 `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++>++(k)` 构造的元素,并使用指向新创建元素的常量引用调用 `f1` ;否则,使用指向等效元素的常量引用调用 `f2` 。 + +[horizontal] +要求;; `value++_++type` 需满足从 `k` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] 的要求。 +返回;; 若发生插入,则返回 `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` 的原值。 +抛出;; 若异常由调用 `hasher` 、 `f1` 或 `f2` 以外的操作抛出,则此函数不产生任何效果。 +并发性;; 阻塞于 `++*++this` 的重哈希。 +注意;; 如果 `nh` 非空,且 `nh` 与容器的分配器不相等,则行为未定义。 + +--- + +==== 擦除 +```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++<++class K++>++` 重载才会参与重载决议。标准库假设 `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++<++std::remove++_++cvref++_++t++<++ExecutionPolicy++>>++` 为 `false` 时, `template++<++class K, class F++>++` 重载才会参与重载决议。 + ++ +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K, class F++>++` 重载才会参与重载决议。标准库假定 `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{plus}{plus}17 并行算法的编译器中可用。 + ++ +仅当 `std::is++_++execution++_++policy++_++v++<++std::remove++_++cvref++_++t++<++ExecutionPolicy++>>++` 为 `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` 已声明, 且其 `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++<++class K++>++` 重载才会参与重载决议。标准库假设 `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++<++class K++>++` 重载才会参与重载决议。标准库假设 `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++<++class K++>++` 重载才会参与重载决议。标准库假设 `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++<++class K++>++` 重载才会参与重载决议。标准库假设 `Hash` 可同时被 `K` 与 `Key` 类型调用,且 `Pred` 为透明的。这支持异构查找,该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + ++ +在存在并发插入操作的情况下,返回值可能无法准确反映函数执行完成后容器的真实状态。 + +--- +=== 桶接口 + +==== bucket++_++count +```c++ + +size_type bucket_count() const noexcept; + +``` + +[horizontal] +返回;; 桶数组的大小。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ + +float load_factor() const noexcept; + +``` + +[horizontal] +返回;; 返回 `static++_++cast++<++float++>++(size())/static++_++cast++<++float++>++(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_node_set_boost_unordered_enable_stats[已启用] 时可用。 + +--- + +==== reset++_++stats +```c++ + +void reset_stats() noexcept; + +``` + +[horizontal] +效果;; 将容器所保存的内部统计信息重置为零。 +注意;; 仅当 xref:reference/stats.adoc#stats[统计计算] 功能 xref:#concurrent_node_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_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..830e126 --- /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; + +} // 命名空间 unordered +} // 命名空间 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..95c108a --- /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..d717a66 --- /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]; + + // 相等比较 + 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); + + // 交换 + 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))); + + // 擦除 + 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 别名 (C++17及以上版本) + namespace pmr { + template, + class Pred = std::equal_to> + using concurrent_flat_map = + boost::unordered::concurrent_flat_map>>; + } // 命名空间 pmr擦除 + +} // 命名空间 unordered + +using unordered::concurrent_flat_map; + +} // 命名空间 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..f43c73b --- /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..f68b461 --- /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]; + + // 相等比较 + 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); + + // 交换 + 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))); + + // 擦除 + 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别名(C++17及以上) + namespace pmr { + template, + class Pred = std::equal_to> + using concurrent_flat_set = + boost::unordered::concurrent_flat_set>; + } // 命名空间 pmr + +} // 命名空间 unordered + +using unordered::concurrent_flat_set; + +} // 命名空间 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..057c96e --- /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..001550a --- /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]; + + // 相等比较 + 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); + + // 交换 + 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))); + + // 擦除 + 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别名(C++17及以上) + namespace pmr { + template, + class Pred = std::equal_to> + using concurrent_node_map = + boost::unordered::concurrent_node_map>>; + } // 命名空间 pmr + +} // 命名空间 unordered + +using unordered::concurrent_node_map; + +} // 命名空间 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..29f3c69 --- /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..096dc13 --- /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]; + + // 相等比较 + 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); + + // 交换 + 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))); + + // 擦除 + 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别名(C++17及以上) + namespace pmr { + template, + class Pred = std::equal_to> + using concurrent_node_set = + boost::unordered::concurrent_node_set>; + } // 命名空间 pmr + +} // 命名空间 unordered + +using unordered::concurrent_node_set; + +} // 命名空间 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..2b2574d --- /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..7b19752 --- /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]; + + // 相等比较 + 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); + + // 交换 + 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))); + + // 擦除 + 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 别名(C++17 及以上) + namespace pmr { + template, + class Pred = std::equal_to> + using unordered_flat_map = + boost::unordered::unordered_flat_map>>; + } // 命名空间 pmr + +} // 命名空间 unordered + +using unordered::unordered_flat_map; + +} // 命名空间 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..54c2357 --- /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..6fc3e97 --- /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]; + + // 相等比较 + 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); + + // 交换 + 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))); + + // 擦除 + 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 别名(C++17 及以上) + namespace pmr { + template, + class Pred = std::equal_to> + using unordered_flat_set = + boost::unordered::unordered_flat_set>; + } // 命名空间 pmr + +} // 命名空间 unordered + +using unordered::unordered_flat_set; + +} // 命名空间 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..9b82999 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_unordered_map_fwd_zh_Hans.adoc @@ -0,0 +1,6 @@ +[#header_unordered_map_fwd] +== `++<++boost/unordered/unordered++_++map++_++fwd.hpp++>++` 概要 + +:idprefix: header_unordered_map_fwd_ + +前向声明 xref:reference/header_unordered_map.adoc[`++<++boost/unordered/unordered++_++map.hpp++>++`] 中的所有定义。 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..3f26834 --- /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..5a89520 --- /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]; + + // 相等比较 + 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); + + // 交换 + template + void xref:reference/unordered_map.adoc#unordered_map_swap_2[swap](unordered_map& x, + unordered_map& y) + noexcept(noexcept(x.swap(y))); + + // 擦除 + 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]; + + // 相等比较 + 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); + + // 交换 + template + void xref:reference/unordered_multimap.adoc#unordered_multimap_swap_2[swap](unordered_multimap& x, + unordered_multimap& y) + noexcept(noexcept(x.swap(y))); + + // 擦除 + template + typename unordered_multimap::size_type + xref:reference/unordered_multimap.adoc#unordered_multimap_erase_if[erase_if](unordered_multimap& c, Predicate pred); + + // Pmr别名(C++17及以上) + 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>>; + } // 命名空间 pmr + +} // 命名空间 unordered + +using unordered::unordered_map; +using unordered::unordered_multimap; + +} // 命名空间 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..5d182ea --- /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..5c30bd9 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/header_unordered_node_map_zh_Hans.adoc @@ -0,0 +1,57 @@ +[#header_unordered_node_map] +== `++<++boost/unordered/unordered++_++node++_++map.hpp++>++` 概要 + +: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]; + + // 相等比较 + 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); + + // 交换 + 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))); + + // 擦除 + 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别名(C++17及以上版本) + namespace pmr { + template, + class Pred = std::equal_to> + using unordered_node_map = + boost::unordered::unordered_node_map>>; + } // 命名空间 pmr + +} // 命名空间 unordered + +using unordered::unordered_node_map; + +} // 命名空间 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..295c580 --- /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..2f7af6e --- /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]; + + // 相等比较 + 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); + + // 交换 + 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))); + + // 擦除 + 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别名(C++17及以上) + namespace pmr { + template, + class Pred = std::equal_to> + using unordered_node_set = + boost::unordered::unordered_node_set>; + } // 命名空间 pmr + +} // 命名空间 unordered + +using unordered::unordered_node_set; + +} // 命名空间 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..293f2e5 --- /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..1d2d87c --- /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..84c4f42 --- /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]; + + // 相等比较 + 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); + + // 交换 + template + void xref:reference/unordered_set.adoc#unordered_set_swap_2[swap](unordered_set& x, + unordered_set& y) + noexcept(noexcept(x.swap(y))); + + // 擦除 + 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]; + + // 相等比较 + 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); + + // 交换 + template + void xref:reference/unordered_multiset.adoc#unordered_multiset_swap_2[swap](unordered_multiset& x, + unordered_multiset& y) + noexcept(noexcept(x.swap(y))); + + // 擦除 + template + typename unordered_multiset::size_type + xref:reference/unordered_multiset.adoc#unordered_multiset_erase_if[erase_if](unordered_multiset& c, Predicate pred); + + // Pmr别名(C++17及以上) + 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>; + } // 命名空间 pmr + +} // 命名空间 unordered + +using unordered::unordered_set; +using unordered::unordered_multiset; + +} // 命名空间 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..de6ca3e --- /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..f3af8e2 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/unordered_flat_map_zh_Hans.adoc @@ -0,0 +1,1648 @@ +[#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 + +namespace boost { +namespace unordered { + + template, + class Pred = std::equal_to, + class Allocator = std::allocator>> + class unordered_flat_map { + public: + // 类型定义 + 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] + + // 构造/复制/销毁 + 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; + + // 迭代器 + 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; + + // 容量 + ++[[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; + + // 修改器 + 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); + + // 观察器 + hasher xref:#unordered_flat_map_hash_function[hash_function]() const; + key_equal xref:#unordered_flat_map_key_eq[key_eq]() const; + + // 映射操作 + 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; + + // 元素访问 + 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; + + // 桶接口 + size_type xref:#unordered_flat_map_bucket_count[bucket_count]() const noexcept; + + // 哈希策略 + 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); + + // 统计(若已启用) + stats xref:#unordered_flat_map_get_stats[get_stats]() const; + void xref:#unordered_flat_map_reset_stats[reset_stats]() noexcept; + }; + + // 推导指引 + 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>; + +} // 命名空间 unordered +} // 命名空间 boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_键_ +.2+|`Key` 和 `T` +必须满足 https://en.cppreference.com/w/cpp/named_req/MoveConstructible[可移动构造] 要求。容器中的 +`std::pair++<++const Key, T++>++` 必须可从任何可转换为它的 `std::pair` +对象 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] ,且必须可从容器中 https://en.cppreference.com/w/cpp/named_req/Erasable[擦除] 。 + +|_T_ + +|_Hash_ +|一元函数对象类型,用作 `Key` 的哈希函数。它接受一个类型为 `Key` +的参数,并返回一个 `std::size++_++t` 类型的值。 + +|_谓词_ +|二元函数对象,用于在 `Key` +类型的值上建立等价关系。它接受两个类型为 `Key` 的参数,并返回一个 `bool` +类型的值。 + +|_分配器_ +|一种分配器,其值类型与容器的值类型相同,是一种支持使用 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); + +``` + +构造一个至少包含 `n` 个桶的空容器,使用 `hf` 作为哈希函数、默认的键相等性谓词以及 `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` 可同等地转换为 `const value++_++type&` 和 `const 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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能会导致迭代器、指针和引用失效,但仅当插入操作导致负载大于最大负载时。 + +--- + +==== 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` 的构造函数。 + +可能会导致迭代器、指针和引用失效,但仅当插入操作导致负载因子超过最大负载因子时才会发生。 + +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 隐式转换时, `template++<++class K, class... Args++>++` 重载才会参与重载决议。标准库假定 `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` 的构造函数。 + +可能会导致迭代器、指针和引用失效,但仅当插入操作导致负载因子超过最大负载因子时才会发生。 + +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 隐式转换时, `template++<++class K, class... Args++>++` 重载才会参与重载决议。标准库假定 `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++<++M++>++(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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能会导致迭代器、指针和引用失效,但仅当插入操作导致负载因子超过最大负载因子时才会发生。 + ++ +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K, class M++>++` 才会参与重载决议。标准库假设 `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++<++M++>++(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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能会导致迭代器、指针和引用失效,但仅当插入操作导致负载大于最大负载时。 + ++ +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K, class M++>++` 才会参与重载决议。标准库假设 `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` 抛出异常时,才会抛出异常。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 类型隐式转换时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `Hash` 可同时被 `K` 和 `Key` 类型调用,且 `Pred` 为透明谓词。该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + +--- + +==== 范围擦除 + +```c++ + +iterator erase(const_iterator first, const_iterator last); + +``` + +擦除从 `first` 到 `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` 中的元素执行移动插入操作,并将这些元素从 `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()` 。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `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++<++key++_++type const, mapped++_++type++>++(k, mapped++_++type())` 。 +返回;; 返回一个引用,该引用指向容器中已存在的键等于 `k` 的元素 `x` 的 `x.second` 成员;若元素不存在,则指向新插入元素的该成员。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能会导致迭代器、指针和引用失效,但仅当插入操作导致负载大于最大负载时。 + ++ +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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] +返回;; 返回一个引用,该引用指向键等价于 `k` 的(唯一)元素 `x` 的 `x.second` 成员。 +抛出;; 如果不存在这样的元素,则抛出类型为 `std::out++_++of++_++range` 的异常对象。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `Hash` 可同时被 `K` 和 `Key` 类型调用,且 `Pred` 为透明谓词。该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + +--- + +=== 桶接口 + +==== bucket++_++count +```c++ + +size_type bucket_count() const noexcept; + +``` + +[horizontal] +返回;; 桶数组的大小。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ + +float load_factor() const noexcept; + +``` + +[horizontal] +返回;; 返回 `static++_++cast++<++float++>++(size())/static++_++cast++<++float++>++(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_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] +返回;; 被擦除的元素数量。 +注意;; 等价于: + ++ +```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++_++flat++_++map` 可通过本组件库提供的 API,借助 link:../../../../../serialization/index.html[Boost.Serialization] 进行归档/检索。支持常规归档与 XML 归档两种格式。 + +==== 将 unordered++_++flat++_++map 保存到归档 + +将 `unordered++_++flat++_++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[可默认构造] 要求)。 + +--- + +==== 从归档加载 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..c1a84f4 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/unordered_flat_set_zh_Hans.adoc @@ -0,0 +1,1394 @@ +[#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()` 不是常数时间复杂度操作。 + - 未提供用于桶管理(除 `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] + + // 构造/复制/销毁 + 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; + + // 迭代器 + 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; + + // 容量 + ++[[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; + + // 修改器 + 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); + + // 观察器 + hasher xref:#unordered_flat_set_hash_function[hash_function]() const; + key_equal xref:#unordered_flat_set_key_eq[key_eq]() const; + + // 集合操作 + 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; + + // 桶接口 + size_type xref:#unordered_flat_set_bucket_count[bucket_count]() const noexcept; + + // 哈希策略 + 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); + + // 统计(如果启用) + stats xref:#unordered_flat_set_get_stats[get_stats]() const; + void xref:#unordered_flat_set_reset_stats[reset_stats]() noexcept; + }; + + // 推导指引 + 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>; + +} // 命名空间 unordered +} // 命名空间 boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_键_ +|`Key` 必须满足 +https://en.cppreference.com/w/cpp/named_req/MoveInsertable[可移动插入] +到容器中的要求,且需满足从容器中 +https://en.cppreference.com/w/cpp/named_req/Erasable[可擦除] 的要求。 + +|_Hash_ +|一元函数对象类型,用作 `Key` 的哈希函数。它接受一个类型为 `Key` +的参数,并返回一个 `std::size++_++t` 类型的值。 + +|_谓词_ +|二元函数对象,用于在 `Key` +类型的值上建立等价关系。它接受两个类型为 `Key` 的参数,并返回一个 `bool` +类型的值。 + +|_分配器_ +|一种分配器,其值类型与容器的值类型相同,是一种支持使用 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_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_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 + 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[可默认构造] 要求。 + +--- + +==== 复制构造函数 +```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[启用] ,则同时转移 `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[可默认构造] 要求。 + +--- + +==== 分配器构造函数 +```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[启用] ,则当 `a == other.get++_++allocator()` 时,同时转移其内部统计信息,并始终调用 `other.reset++_++stats()` 。 + +--- + +==== 从 concurrent++_++flat++_++set 的移动构造函数 + +```c++ + +unordered_flat_set(concurrent_flat_set&& other); + +``` + +从 #xref:#concurrent_flat_set[`concurrent_flat_set`][`concurrent++_++flat++_++set`] 移动构造。 `other` 的内部桶数组直接转移到新容器。哈希函数、谓词和分配器从 `other` 移动构造。如果统计功能已 xref:#unordered_flat_set_boost_unordered_enable_stats[启用] ,则同时转移 `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[可默认构造] 要求。 + +--- + +==== 带分配器的桶数构造函数 +```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[可默认构造] 要求。 + +--- + +==== 带哈希函数和分配器的桶数构造函数 +```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[可默认构造] 要求。 + +--- + +==== 带桶数和分配器的迭代器范围构造函数 +[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[可默认构造] 要求。 + +--- + +==== 带桶数和哈希函数的迭代器范围构造函数 +[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[可默认构造] 要求。 + +--- + +==== 带分配器的初始化列表构造函数 + +```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[可默认构造] 要求。 + +--- + +==== 带桶数和分配器的初始化列表构造函数 + +```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[可默认构造] 要求。 + +--- + +==== 带桶数、哈希函数和分配器的初始化列表构造函数 + +```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[可默认构造] 要求。 + +--- + +=== 析构函数 + +```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[可复制插入] 要求 + +--- + +==== 移动赋值 +```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` +中元素的移动构造副本。若已启用统计功能,则当且仅当最终分配器与 +`other.get++_++allocator()` 相等时,同时转移其内部统计信息,并始终调用 +`other.reset++_++stats()` 。 + +--- + +==== 初始化列表赋值 +```c++ + +unordered_flat_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] +返回;; 返回一个指向容器第一个元素的 `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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能会导致迭代器、指针和引用失效,但仅当插入操作导致负载大于最大负载时。 + + +--- + +==== 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++>++(k)` 构造的元素。 + +[horizontal] +要求;; `value++_++type` 需满足从 `k` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] 的要求。 +返回;; 若插入成功,则返回类型的布尔分量为 true。 + ++ +若插入成功,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能会导致迭代器、指针和引用失效,但仅当插入操作导致负载大于最大负载时。 + ++ +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `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++>++(k)` 构造的元素。 + +`hint` 是一个关于元素插入位置的提示,本实现将忽略该提示。 + +[horizontal] +要求;; `value++_++type` 需满足从 `k` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] 的要求。 +返回;; 若插入成功,则返回类型的布尔分量为 true。 + ++ +若插入成功,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能会导致迭代器、指针和引用失效,但仅当插入操作导致负载大于最大负载时。 + ++ +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能会导致迭代器、指针和引用失效,但仅当插入操作导致负载大于最大负载时。 + +--- + +==== 通过位置擦除 + +[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` 抛出异常时,才会抛出异常。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 类型隐式转换时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `Hash` 可同时被 `K` 和 `Key` 类型调用,且 `Pred` 为透明谓词。该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + +--- + +==== 范围擦除 + +```c++ + +iterator erase(const_iterator first, const_iterator last); + +``` + +擦除从 `first` 到 `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] +后置条件;; `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()` 。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `Hash` 可同时被 `K` 和 `Key` 类型调用,且 `Pred` 为透明谓词。该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + +--- + +=== 桶接口 + +==== bucket++_++count +```c++ + +size_type bucket_count() const noexcept; + +``` + +[horizontal] +返回;; 桶数组的大小。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ + +float load_factor() const noexcept; + +``` + +[horizontal] +返回;; 返回 `static++_++cast++<++float++>++(size())/static++_++cast++<++float++>++(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_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[可默认构造] 要求的类型)。 + +--- + +==== 从归档加载 unordered++_++flat++_++set + +删除 `unordered++_++flat++_++set` 容器 `x` 中存在的所有现有元素,并从归档(XML 归档) `ar` 中插入原始 `unordered++_++flat++_++set` 容器 `other` 的元素副本,这些副本是从 `ar` 所读取的存储中恢复的。 + +[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` 指向原始 `iterator` ( `const++_++iterator` )所恢复的位置。该原始迭代器已被保存到由归档(XML 归档) `ar` 读取的存储中。 + +[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..eff4b36 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/unordered_map_zh_Hans.adoc @@ -0,0 +1,1950 @@ +[#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>; + +} // 命名空间 unordered +} // 命名空间 boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_键_ +|`Key` +需满足从容器中 https://en.cppreference.com/w/cpp/named_req/Erasable[可擦除] 的要求(即 +`allocator++_++traits` 能够销毁该对象)。 + +|_T_ +|`T` +需满足从容器 https://en.cppreference.com/w/cpp/named_req/Erasable[可擦除] +的要求(即 `allocator++_++traits` 能够销毁该对象)。 + +|_Hash_ +|一元函数对象类型,用作 `Key` 的哈希函数。它接受一个类型为 `Key` +的参数,并返回一个 `std::size++_++t` 类型的值。 + +|_谓词_ +|一种二元函数对象,用于在 `Key` +类型的值上建立等价关系。它接受两个类型为 `Key` +的参数,并返回一个布尔类型的值。 + +|_分配器_ +|一种分配器,其值类型与容器的值类型相同,是一种支持使用 https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[花式指针] 的分配器。 + +|=== + +元素被组织到多个桶中。具有相同哈希码的键存储在同一个桶内。 + +桶的数量可通过调用插入操作来自动增加,也可通过调用重哈希操作来调整。 + +=== 配置宏 + +==== `BOOST++_++UNORDERED++_++ENABLE++_++SERIALIZATION++_++COMPATIBILITY++_++V0` + +全局定义此宏,以支持加载在 Boost.Serialization 归档中保存的 `unordered++_++map` 容器,这些归档是保存在 Boost 1.84 之前的版本中。 + +=== 类型定义 + +[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); + +``` +移动赋值运算符。 + +如果 `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` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原位构造] 到 `X` 容器的要求。 +返回;; 若插入成功,则返回类型的布尔分量为 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` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原位构造] 到 `X` 容器的要求。 +返回;; 若插入成功,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是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[可复制插入] 要求。 +返回;; 若插入成功,则返回类型的布尔分量为 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(P&& obj); + +``` + +通过执行 `emplace(std::forward++<++P++>++(value))` 将元素插入容器。 + +仅当 `std::is++_++constructible++<++value++_++type, P&&++>++::value` 为 `true` 时,才会参与重载决议。 + +[horizontal] +返回;; 若插入成功,则返回类型的布尔分量为 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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是Boost.Unordered唯一支持的用法)是指向具有相同键的现有元素。 + ++ +可能引发迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +指向元素的指针和引用永远不会失效。 + +--- + +==== 带提示的原地插入 + +```c++ + +template iterator insert(const_iterator hint, P&& obj); + +``` + +通过执行 `emplace++_++hint(hint, std::forward++<++P++>++(value))` 将元素插入容器。 + +仅当 `std::is++_++constructible++<++value++_++type, P&&++>++::value` 为 `true` 时,才会参与重载决议。 + +`hint` 是插入元素位置的建议。 + +[horizontal] +返回;; 若插入成功,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是Boost.Unordered唯一支持的用法)是指向具有相同键的现有元素。 + ++ +可能引发迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +指向元素的指针和引用永远不会失效。 + +--- + +==== 迭代器范围插入 +```c++ + +template void insert(InputIterator first, InputIterator last); + +``` + +将元素范围插入容器中。仅当容器中不存在等价键的元素时,才会插入相应元素。 + +[horizontal] +要求;; `value++_++type` 必须能够通过 `++*++first` 进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] 到 `X` 中。 +抛出;; 若异常由 `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] +返回;; 若插入成功,则返回类型的布尔分量为 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`` 的构造函数。 + +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 + +指向元素的指针和引用永远不会失效。 + +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 隐式转换时, `template++<++class K, class... Args++>++` 重载才会参与重载决议。标准库假定 `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`` 的构造函数。 + +标准对提示的含义相当模糊。但唯一实用的使用方式(也是 Boost.Unordered 支持的唯一方式)是指向具有相同键的现有元素。 + +可能使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 + +指向元素的指针和引用永远不会失效。 + +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 隐式转换时, `template++<++class K, class... Args++>++` 重载才会参与重载决议。标准库假定 `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++<++M++>++(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] +返回;; 若插入成功,则返回类型的布尔分量为 true。 + ++ +若插入成功,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能引发迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +指向元素的指针和引用永远不会失效。 + ++ +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K, class M++>++` 才会参与重载决议。标准库假设 `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++<++M++>++(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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是Boost.Unordered唯一支持的用法)是指向具有相同键的现有元素。 + ++ +可能引发迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +指向元素的指针和引用永远不会失效。 + ++ +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K, class M++>++` 才会参与重载决议。标准库假设 `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` 容器中。 + ++ +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 类型隐式转换时, `template++<++class 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 以外的操作抛出,则此函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是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` 抛出异常时,才会抛出异常。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 类型隐式转换时, `template++<++class 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++_++return++_++void +```c++ + +void erase_return_void(const_iterator position); + +``` + +擦除由 `position` 指向的元素。 + +[horizontal] +抛出;; 仅当 `hasher` 或 `key++_++equal` 抛出异常时,本函数才会抛出异常。 + ++ +在此实现中,此重载不会调用任一函数对象的方法,因此不会抛出异常,但在其他实现中可能并非如此。 +注意;; 此方法被实现是因为从擦除操作返回下一个元素的迭代器代价高昂,但容器经过重新设计后该问题已得到解决,因此该方法现已被弃用。 + +--- + +==== 交换 +```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{plus}{plus}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` 容器,提取其中所有不包含在 `++*++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` 的成员函数模板。 + ++ +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `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++<++key++_++type const, mapped++_++type++>++(k, mapped++_++type())` 。 +返回;; 返回一个引用,该引用指向容器中已存在的键等于 `k` 的元素 `x` 的 `x.second` 成员;若元素不存在,则指向新插入元素的该成员。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能引发迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +指向元素的指针和引用永远不会失效。 + ++ +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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] +返回;; 返回一个引用,该引用指向键等价于 `k` 的(唯一)元素 `x` 的 `x.second` 成员。 +抛出;; 如果不存在这样的元素,则抛出类型为 `std::out++_++of++_++range` 的异常对象。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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++<++float++>++::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{plus}{plus}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++<++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[可默认构造] 要求)。 + +--- + +==== 从归档加载 unordered++_++map + +删除 `unordered++_++map` 容器 `x` 的所有现有元素,并从归档(XML 归档) `ar` 中插入原始 `unordered++_++map` `other` 的元素副本,这些副本是从 `ar` 读取的存储中恢复的。 + +[horizontal] +要求;; `value++_++type` 必须能够从 `(std::remove++_++const++<++key++_++type++>++::type&&, std::remove++_++const++<++mapped++_++type++>++::type&&)` 进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] ,且 `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++_++map` `x` 必须先前已保存到 `ar` ,并且在保存 `x` 和保存 `it` 期间不得对 `x` 执行任何修改操作。 + +--- + +==== 从归档加载迭代器/常量迭代器 + +使 `iterator` ( `const++_++iterator` ) `it` 指向原始 `iterator` ( `const++_++iterator` )所恢复的位置。该原始迭代器已被保存到由归档(XML 归档) `ar` 读取的存储中。 + +[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..9fb7d0a --- /dev/null +++ b/doc/modules/ROOT/pages/reference/unordered_multimap_zh_Hans.adoc @@ -0,0 +1,1658 @@ +[#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>; + +} // 命名空间 unordered +} // 命名空间 boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_键_ +|`Key` +需满足从容器中 https://en.cppreference.com/w/cpp/named_req/Erasable[可擦除] 的要求(即 +`allocator++_++traits` 能够销毁该对象)。 + +|_T_ +|`T` +需满足从容器 https://en.cppreference.com/w/cpp/named_req/Erasable[可擦除] +的要求(即 `allocator++_++traits` 能够销毁该对象)。 + +|_Hash_ +|一元函数对象类型,用作 `Key` 的哈希函数。它接受一个类型为 `Key` +的参数,并返回一个 `std::size++_++t` 类型的值。 + +|_谓词_ +|一种二元函数对象,用于在 `Key` +类型的值上建立等价关系。它接受两个类型为 `Key` +的参数,并返回一个布尔类型的值。 + +|_分配器_ +|一种分配器,其值类型与容器的值类型相同,是一种支持使用 https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[花式指针] 的分配器。 + +|=== + +元素被组织到多个桶中。具有相同哈希码的键存储在同一个桶内。 + +桶的数量可通过调用插入操作来自动增加,也可通过调用重哈希操作来调整。 + +=== 配置宏 + +==== `BOOST++_++UNORDERED++_++ENABLE++_++SERIALIZATION++_++COMPATIBILITY++_++V0` + +全局定义此宏以支持加载在 Boost 1.84 之前版本中保存到 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); + +``` + +构造一个至少包含 `n` 个桶的空容器,使用 `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 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); + +``` +移动赋值运算符。 + +如果 `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[可复制赋值] 的要求。 + +=== 迭代器 + +==== 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 iterator emplace(Args&&... args); + +``` + +向容器中插入一个由参数 `args` 构造的对象。 + +[horizontal] +要求;; `value++_++type` 必须满足从 `args` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原位构造] 到 `X` 容器的要求。 +返回;; 返回一个指向插入元素的迭代器。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能引发迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +指向元素的指针和引用永远不会失效。 + +--- + +==== emplace++_++hint +```c++ + +template iterator emplace_hint(const_iterator position, Args&&... args); + +``` + +向容器中插入一个由参数 args 构造的对象。 + +`position` 是一个关于插入元素位置的建议。 + +[horizontal] +要求;; `value++_++type` 必须满足从 `args` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原位构造] 到 `X` 容器的要求。 +返回;; 返回一个指向插入元素的迭代器。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是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++<++P++>++(value))` 将元素插入容器。 + +仅当 `std::is++_++constructible++<++value++_++type, P&&++>++::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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是Boost.Unordered唯一支持的用法)是指向具有相同键的现有元素。 + ++ +可能引发迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +指向元素的指针和引用永远不会失效。 + +--- + +==== 带提示的原地插入 +```c++ + +template iterator insert(const_iterator hint, P&& obj); + +``` + +通过执行 `emplace++_++hint(hint, std::forward++<++P++>++(value))` 将元素插入容器。 + +仅当 `std::is++_++constructible++<++value++_++type, P&&++>++::value` 为 `true` 时,才会参与重载决议。 + +`hint` 是插入元素位置的建议。 + +[horizontal] +返回;; 返回一个指向插入元素的迭代器。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是Boost.Unordered唯一支持的用法)是指向具有相同键的现有元素。 + ++ +可能引发迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +指向元素的指针和引用永远不会失效。 + +--- + +==== 迭代器范围插入 +```c++ + +template void insert(InputIterator first, InputIterator last); + +``` + +将元素范围插入容器。 + +[horizontal] +要求;; `value++_++type` 必须能够通过 `++*++first` 进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] 到 `X` 中。 +抛出;; 若异常由 `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` 容器中。 + ++ +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 类型隐式转换时, `template++<++class 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 以外的操作抛出,则此函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是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` 抛出异常时,才会抛出异常。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 类型隐式转换时, `template++<++class 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++_++return++_++void +```c++ + +void erase_return_void(const_iterator position); + +``` + +擦除由 `position` 指向的元素。 + +[horizontal] +抛出;; 仅当 `hasher` 或 `key++_++equal` 抛出异常时,本函数才会抛出异常。 + ++ +在此实现中,此重载不会调用任一函数对象的方法,因此不会抛出异常,但在其他实现中可能并非如此。 +注意;; 此方法被实现是因为从擦除操作返回下一个元素的迭代器代价高昂,但容器经过重新设计后该问题已得到解决,因此该方法现已被弃用。 + +--- + +==== 交换 +```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{plus}{plus}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` 容器,提取其中所有节点并将其插入到 `++*++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` 的成员函数模板。 + ++ +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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++<++float++>++::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 +----- + +=== 相等性比较 + +==== operator== +```c++ + +template + bool operator==(const unordered_multimap& x, + const unordered_multimap& y); + +``` + +若 `x.size() == y.size()` ,且对于 `x` 中的每个等效键组, `y` 中均存在一个对应相同键的组,且该组是原组的排列(使用 `operator==` 比较值类型),则返回 `true` 。 + +[horizontal] +注意;; 如果两个容器的相等性谓词不等价,则行为未定义。 + +--- + +==== operator!= +```c++ + +template + bool operator!=(const unordered_multimap& x, + const unordered_multimap& y); + +``` + +若 `x.size() != y.size()` ,或存在 `x` 中的某个等效键组,在 `y` 中不存在对应的相同键组,或该组不是原组的排列(使用 `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{plus}{plus}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++<++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[可默认构造] 要求)。 + +--- + +==== 从归档加载 unordered++_++multimap + +删除 `unordered++_++multimap` 容器 `x` 中的所有预先存在的元素,并从归档(XML 归档) `ar` 中插入原始 `unordered++_++multimap` 容器 `other` 的元素副本,这些副本是从 `ar` 所读取的存储中恢复的。 + +[horizontal] +要求;; `value++_++type` 必须能够从 `(std::remove++_++const++<++key++_++type++>++::type&&, std::remove++_++const++<++mapped++_++type++>++::type&&)` 进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] ,且 `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++_++multimap` `x` 必须先前已保存到 `ar` ,且在保存 `x` 与保存 `it` 期间不得对 `x` 执行任何修改操作。 + +--- + +==== 从归档加载迭代器/常量迭代器 + +使 `iterator` ( `const++_++iterator` ) `it` 指向原始 `iterator` ( `const++_++iterator` )所恢复的位置。该原始迭代器已被保存到由归档(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..4c3dc00 --- /dev/null +++ b/doc/modules/ROOT/pages/reference/unordered_multiset_zh_Hans.adoc @@ -0,0 +1,1584 @@ +[#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: + // 类型 + 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_; + + // 构造/复制/销毁 + 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; + + // 迭代器 + 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; + + // 容量 + ++[[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; + + // 修改器 + 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); + + // 观察器 + hasher xref:#unordered_multiset_hash_function[hash_function]() const; + key_equal xref:#unordered_multiset_key_eq[key_eq]() const; + + // 集合操作 + 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; + + // 桶接口 + 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; + + // 哈希策略 + 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); + }; + + // 推导指引 + 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>; + +} // 命名空间 unordered +} // 命名空间 boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_键_ +|`Key` +需满足从容器中 https://en.cppreference.com/w/cpp/named_req/Erasable[可擦除] 的要求(即 +`allocator++_++traits` 能够销毁该对象)。 + +|_Hash_ +|一元函数对象类型,用作 `Key` 的哈希函数。它接受一个类型为 `Key` +的参数,并返回一个 `std::size++_++t` 类型的值。 + +|_谓词_ +|一种二元函数对象,用于在 `Key` +类型的值上建立等价关系。它接受两个类型为 `Key` +的参数,并返回一个布尔类型的值。 + +|_分配器_ +|一种分配器,其值类型与容器的值类型相同,是一种支持使用 https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[花式指针] 的分配器。 + +|=== + +元素被组织到多个桶中。具有相同哈希码的键存储在同一个桶内,且等效键的元素彼此相邻存储。 + +桶的数量可通过调用插入操作来自动增加,也可通过调用重哈希操作来调整。 + +=== 配置宏 + +==== `BOOST++_++UNORDERED++_++ENABLE++_++SERIALIZATION++_++COMPATIBILITY++_++V0` + +全局定义此宏,以支持加载 Boost 1.84 之前版本保存到 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[可复制赋值] 的要求。 + +--- + +=== 迭代器 + +==== 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 iterator emplace(Args&&... args); + +``` + +向容器中插入一个由参数 args 构造的对象。 + +[horizontal] +要求;; `value++_++type` 必须满足从 `args` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原位构造] 到 `X` 容器的要求。 +返回;; 返回一个指向插入元素的迭代器。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能引发迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +指向元素的指针和引用永远不会失效。 + +--- + +==== emplace++_++hint +```c++ + +template iterator emplace_hint(const_iterator position, Args&&... args); + +``` + +向容器中插入一个由参数 args 构造的对象。 + +`hint` 是插入元素位置的建议。 + +[horizontal] +要求;; `value++_++type` 必须满足从 `args` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原位构造] 到 `X` 容器的要求。 +返回;; 返回一个指向插入元素的迭代器。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是Boost.Unordered唯一支持的用法)是指向具有相同键的现有元素。 + ++ +可能引发迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +指向元素的指针和引用永远不会失效。 + +--- + +==== 迭代器范围插入 +```c++ + +template void insert(InputIterator first, InputIterator last); + +``` + +将元素范围插入容器。 + +[horizontal] +要求;; `value++_++type` 必须能够通过 `++*++first` 进行 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] 到 `X` 中。 +抛出;; 若异常由 `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` 容器中。 + ++ +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 类型隐式转换时, `template++<++class 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 以外的操作抛出,则此函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是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` 抛出异常时,才会抛出异常。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 类型隐式转换时, `template++<++class 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++_++return++_++void +```c++ + +void erase_return_void(const_iterator position); + +``` + +擦除由 `position` 指向的元素。 + +[horizontal] +抛出;; 仅当 `hasher` 或 `key++_++equal` 抛出异常时,本函数才会抛出异常。 + ++ +在此实现中,此重载不会调用任一函数对象的方法,因此不会抛出异常,但在其他实现中可能并非如此。 +注意;; 此方法被实现是因为从擦除操作返回下一个元素的迭代器代价高昂,但容器经过重新设计后该问题已得到解决,因此该方法现已被弃用。 + +--- + +==== 交换 +```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{plus}{plus}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` 容器,提取其中所有节点并将其插入到 `++*++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 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` 的成员函数模板。 + ++ +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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++<++float++>++::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_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{plus}{plus}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 序列化),且需要支持 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++_++UNORDERED++_++ENABLE++_++SERIALIZATION++_++COMPATIBILITY++_++V0` 才能成功执行此操作;否则将抛出异常。 + +--- + +==== 将迭代器/常量迭代器保存到归档 + +将 `iterator` ( `const++_++iterator` )常量迭代器 `it` 的位置信息保存到归档(XML 归档) `ar` 中。 `it` 可以是 `end()` 迭代器。 + +[horizontal] +要求;; `it` 所指向的 `unordered++_++multiset` 容器 `x` 必须先前已保存至 `ar` ,且在保存 `x` 与保存 `it` 期间不得对 `x` 执行任何修改操作。 + +--- + +==== 从归档加载迭代器/常量迭代器 + +使 `iterator` ( `const++_++iterator` ) `it` 指向原始 `iterator` ( `const++_++iterator` )所恢复的位置。该原始迭代器已被保存到由归档(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..277ec4f --- /dev/null +++ b/doc/modules/ROOT/pages/reference/unordered_node_map_zh_Hans.adoc @@ -0,0 +1,1755 @@ +[#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: + // 类型定义 + 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] + + // 构造/复制/销毁 + 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; + + // 迭代器 + 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; + + // 容量 + ++[[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; + + // 修改器 + 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); + + // 观察器 + hasher xref:#unordered_node_map_hash_function[hash_function]() const; + key_equal xref:#unordered_node_map_key_eq[key_eq]() const; + + // 映射操作 + 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; + + // 元素访问 + 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; + + // 桶接口 + size_type xref:#unordered_node_map_bucket_count[bucket_count]() const noexcept; + + // 哈希策略 + 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); + + // 统计(若启用) + stats xref:#unordered_node_map_get_stats[get_stats]() const; + void xref:#unordered_node_map_reset_stats[reset_stats]() noexcept; + }; + + // 推导指引 + 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>; + +} // 命名空间 unordered +} // 命名空间 boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_键_ +.2+|`std::pair++<++const Key, T++>++` 必须可从任何可转换为它的 +`std::pair` +对象 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] 到容器中,并且必须可从容器中 https://en.cppreference.com/w/cpp/named_req/Erasable[擦除] 。 + +|_T_ + +|_Hash_ +|一元函数对象类型,用作 `Key` 的哈希函数。它接受一个类型为 `Key` +的参数,并返回一个 `std::size++_++t` 类型的值。 + +|_谓词_ +|二元函数对象,用于在 `Key` +类型的值上建立等价关系。它接受两个类型为 `Key` 的参数,并返回一个 `bool` +类型的值。 + +|_分配器_ +|一种分配器,其值类型与容器的值类型相同,是一种支持使用 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` 。 + +迭代器类别至少为前向迭代器。 + +--- + +[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[启用] ,则转移 `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[启用] ,则当 `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`][`concurrent++_++node++_++map`] 移动构造。将 `other` 的内部桶数组直接转移至新容器。哈希函数、谓词和分配器均从 `other` 移动构造。若统计功能已 xref:#unordered_node_map_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); + +``` + +构造一个至少包含 `n` 个桶的空容器,使用 `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 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[已启用] ,则当且仅当最终分配器与 +`other.get++_++allocator()` 相等时,同时转移其内部统计信息,并始终调用 +`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] +返回;; `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` 进行检查。当 `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` 可同等地转换为 `const value++_++type&` 和 `const 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] +返回;; 返回一个从 `position` 、 `inserted` 和 `node` 构造的 `insert++_++return++_++type` 对象: + +* 若 `nh` 为空,则 `inserted` 为 `false` , `position` 为 `end()` ,且 `node` 为空。 +* 否则,若插入操作发生,则 `inserted` 为 `true` , `position` 指向被插入的元素,且 `node` 为空。 +* 若插入操作失败,则 `inserted` 为 `false` , `node` 保留 `nh` 的原始值,且 `position` 指向与 `nh.key()` 等效的元素。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 如果 `nh` 非空,且 `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 +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` 的构造函数。 + +可能会导致迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 隐式转换时, `template++<++class K, class... Args++>++` 重载才会参与重载决议。标准库假定 `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 +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` 的构造函数。 + +可能会导致迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 隐式转换时, `template++<++class K, class... Args++>++` 重载才会参与重载决议。标准库假定 `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++<++M++>++(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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能会导致迭代器失效,但仅当插入操作导致负载超过最大负载时才会发生。 + ++ +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K, class M++>++` 才会参与重载决议。标准库假设 `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++<++M++>++(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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能会导致迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +仅当 `Hash::is++_++transparent` 和 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K, class M++>++` 才会参与重载决议。标准库假设 `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` 抛出异常时,才会抛出异常。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 类型隐式转换时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `Hash` 可同时被 `K` 和 `Key` 类型调用,且 `Pred` 为透明谓词。该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + +--- + +==== 范围擦除 + +```c++ + +iterator erase(const_iterator first, const_iterator last); + +``` + +擦除从 `first` 到 `last` 范围内(包含 `first` ,不包含 `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` 抛出异常时,才会抛出异常。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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()` 。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `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++<++key++_++type const, mapped++_++type++>++(k, mapped++_++type())` 。 +返回;; 返回一个引用,该引用指向容器中已存在的键等于 `k` 的元素 `x` 的 `x.second` 成员;若元素不存在,则指向新插入元素的该成员。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能会导致迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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] +返回;; 返回一个引用,该引用指向键等价于 `k` 的(唯一)元素 `x` 的 `x.second` 成员。 +抛出;; 如果不存在这样的元素,则抛出类型为 `std::out++_++of++_++range` 的异常对象。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `Hash` 可同时被 `K` 和 `Key` 类型调用,且 `Pred` 为透明谓词。该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + +--- + +=== 桶接口 + +==== bucket++_++count +```c++ + +size_type bucket_count() const noexcept; + +``` + +[horizontal] +返回;; 桶数组的大小。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ + +float load_factor() const noexcept; + +``` + +[horizontal] +返回;; 返回 `static++_++cast++<++float++>++(size())/static++_++cast++<++float++>++(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[启用] 时可用。 + +--- + +==== reset++_++stats +```c++ + +void reset_stats() noexcept; + +``` + +[horizontal] +效果;; 将容器维护的内部统计信息清零。 +注意;; 仅当 xref:reference/stats.adoc#stats[统计计算] 功能 xref:#unordered_node_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_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++<++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[可默认构造] 要求)。 + +--- + +==== 从归档加载 unordered++_++node++_++map + +删除 `unordered++_++node++_++map` 容器 `x` 中所有已存在的元素,并从归档(XML 归档) `ar` 中插入原始 `unordered++_++node++_++map` 容器 `other` 的元素副本,这些副本是从 `ar` 所读取的存储中恢复的。 + +[horizontal] +要求;; `key++_++type` 与 `mapped++_++type` 必须能够分别从 `std::remove++_++const++<++key++_++type++>++::type&&` 和 `std::remove++_++const++<++mapped++_++type++>++::type&&` 构造。且需要 `x.key++_++equal()` 在功能上等价于 `other.key++_++equal()` 。 + +--- + +==== 将迭代器/常量迭代器保存到归档 + +将 `iterator` ( `const++_++iterator` )常量迭代器 `it` 的位置信息保存到归档(XML 归档) `ar` 中。 `it` 可以是 `end()` 迭代器。 + +[horizontal] +要求;; `it` 所指向的 `unordered++_++node++_++map` 容器 `x` 必须先前已保存至 `ar` ,且在保存 `x` 与保存 `it` 期间不得对 `x` 执行任何修改操作。 + +--- + +==== 从归档加载迭代器/常量迭代器 + +使 `iterator` ( `const++_++iterator` ) `it` 指向原始 `iterator` ( `const++_++iterator` )所恢复的位置。该原始迭代器已被保存到由归档(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..f796a8f --- /dev/null +++ b/doc/modules/ROOT/pages/reference/unordered_node_set_zh_Hans.adoc @@ -0,0 +1,1505 @@ +[#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: + // 类型 + 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] + + // 构造/复制/销毁 + 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; + + // 迭代器 + 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; + + // 容量 + ++[[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; + + // 修改器 + 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); + + // 观察器 + hasher xref:#unordered_node_set_hash_function[hash_function]() const; + key_equal xref:#unordered_node_set_key_eq[key_eq]() const; + + // 集合操作 + 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; + + // 桶接口 + size_type xref:#unordered_node_set_bucket_count[bucket_count]() const noexcept; + + // 哈希策略 + 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); + + // 统计(如果启用) + stats xref:#unordered_node_set_get_stats[get_stats]() const; + void xref:#unordered_node_set_reset_stats[reset_stats]() noexcept; + }; + + // 推导指引 + 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>; + +} // 命名空间 unordered +} // 命名空间 boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_键_ +|`Key` 必须可从容器中 +https://en.cppreference.com/w/cpp/named_req/Erasable[擦除] 。 + +|_Hash_ +|一元函数对象类型,用作 `Key` 的哈希函数。它接受一个类型为 `Key` +的参数,并返回一个 `std::size++_++t` 类型的值。 + +|_谓词_ +|二元函数对象,用于在 `Key` +类型的值上建立等价关系。它接受两个类型为 `Key` 的参数,并返回一个 `bool` +类型的值。 + +|_分配器_ +|一种分配器,其值类型与容器的值类型相同,是一种支持使用 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` 。 + +迭代器类别至少为前向迭代器。 + +--- + +[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[启用] ,则转移 `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[启用] ,则当 `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`][`concurrent++_++node++_++set`] 移动构造。 `other` 的内部桶数组直接转移给新容器。哈希函数、谓词和分配器均从 `other` 移动构造。若统计功能已 xref:#unordered_node_set_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); + +``` + +构造一个至少包含 `n` 个桶的空容器,使用 `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 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[启用] +,则当且仅当最终分配器与 `other.get++_++allocator()` +相等时,同时转移其内部统计信息,并始终调用 `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] +返回;; 返回一个指向容器第一个元素的 `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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能会导致迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + + +--- + +==== 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++>++(k)` 构造的元素。 + +[horizontal] +要求;; `value++_++type` 需满足从 `k` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] 的要求。 +返回;; 若插入成功,则返回类型的布尔分量为 true。 + ++ +若插入成功,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能会导致迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `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++>++(k)` 构造的元素。 + +`hint` 是一个关于元素插入位置的提示,本实现将忽略该提示。 + +[horizontal] +要求;; `value++_++type` 需满足从 `k` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原地构造] 的要求。 +返回;; 若插入成功,则返回类型的布尔分量为 true。 + ++ +若插入成功,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能会导致迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `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] +返回;; 返回一个从 `position` 、 `inserted` 和 `node` 构造的 `insert++_++return++_++type` 对象: + +* 若 `nh` 为空,则 `inserted` 为 `false` , `position` 为 `end()` ,且 `node` 为空。 +* 否则,若插入操作发生,则 `inserted` 为 `true` , `position` 指向被插入的元素,且 `node` 为空。 +* 若插入操作失败,则 `inserted` 为 `false` , `node` 保留 `nh` 的原始值,且 `position` 指向一个键等价于 `nh.value()` 的元素。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 如果 `nh` 非空,且 `nh` 与容器的分配器不相等,则行为未定义。 + +--- + +==== 带提示的节点插入 +```c++ + +iterator insert(const_iterator hint, node_type&& nh); + +``` + +如果 `nh` 非空,则当且仅当容器中不存在键等价于 `nh.value()` 的元素时,插入关联的元素。如果插入操作发生,则 `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` 抛出异常时,才会抛出异常。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 类型隐式转换时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `Hash` 可同时被 `K` 和 `Key` 类型调用,且 `Pred` 为透明谓词。该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + +--- + +==== 范围擦除 + +```c++ + +iterator erase(const_iterator first, const_iterator last); + +``` + +擦除从 `first` 到 `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` 抛出异常时,才会抛出异常。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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] +返回;; 容器的分配器。 + +--- + +==== 哈希函数 +``` + +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()` 。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `Hash` 可同时被 `K` 和 `Key` 类型调用,且 `Pred` 为透明谓词。该机制支持异构查找,从而避免实例化 `Key` 类型的开销。 + +--- + +=== 桶接口 + +==== bucket++_++count +```c++ + +size_type bucket_count() const noexcept; + +``` + +[horizontal] +返回;; 桶数组的大小。 + +--- + +=== 哈希策略 + +==== 负载因子 +```c++ + +float load_factor() const noexcept; + +``` + +[horizontal] +返回;; 返回 `static++_++cast++<++float++>++(size())/static++_++cast++<++float++>++(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[启用] 时可用. + +--- + +==== reset++_++stats +```c++ + +void reset_stats() noexcept; + +``` + +[horizontal] +效果;; 将容器维护的内部统计信息清零。 +注意;; 仅当 xref:reference/stats.adoc#stats[统计计算] 功能 xref:#unordered_node_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_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 序列化),且需要支持 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()` 。 + +--- + +==== 将迭代器/常量迭代器保存到归档 + +将 `iterator` ( `const++_++iterator` )常量迭代器 `it` 的位置信息保存到归档(XML 归档) `ar` 中。 `it` 可以是 `end()` 迭代器。 + +[horizontal] +要求;; `it` 所指向的 `unordered++_++node++_++set` 容器 `x` 必须先前已保存至 `ar` ,且在保存 `x` 与保存 `it` 期间不得对 `x` 执行任何修改操作。 + +--- + +==== 从归档加载迭代器/常量迭代器 + +使 `iterator` ( `const++_++iterator` ) `it` 指向原始 `iterator` ( `const++_++iterator` )所恢复的位置。该原始迭代器已被保存到由归档(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..f4c9f3e --- /dev/null +++ b/doc/modules/ROOT/pages/reference/unordered_set_zh_Hans.adoc @@ -0,0 +1,1663 @@ +[#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: + // 类型定义 + 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_; + + // 构造/复制/销毁 + 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; + + // 迭代器 + 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; + + // 容量 + ++[[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; + + // 修改器 + 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); + + // 观察器 + hasher xref:#unordered_set_hash_function[hash_function]() const; + key_equal xref:#unordered_set_key_eq[key_eq]() const; + + // 集合操作 + 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; + + // 桶接口 + 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; + + // 哈希策略 + 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); + }; + + // 推导指引 + 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>; + +} // 命名空间 unordered +} // 命名空间 boost +----- + +--- + +=== 描述 + +*模板参数* + +[cols="1,1"] +|=== + +|_键_ +|`Key` +需满足从容器中 https://en.cppreference.com/w/cpp/named_req/Erasable[可擦除] 的要求(即 +`allocator++_++traits` 能够销毁该对象)。 + +|_Hash_ +|一元函数对象类型,用作 `Key` 的哈希函数。它接受一个类型为 `Key` +的参数,并返回一个 `std::size++_++t` 类型的值。 + +|_谓词_ +|一种二元函数对象,用于在 `Key` +类型的值上建立等价关系。它接受两个类型为 `Key` +的参数,并返回一个布尔类型的值。 + +|_分配器_ +|一种分配器,其值类型与容器的值类型相同,是一种支持使用 https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[花式指针] 的分配器。 + +|=== + +元素被组织到多个桶中。具有相同哈希码的键存储在同一个桶内。 + +桶的数量可通过调用插入操作来自动增加,也可通过调用重哈希操作来调整。 + +=== 配置宏 + +==== `BOOST++_++UNORDERED++_++ENABLE++_++SERIALIZATION++_++COMPATIBILITY++_++V0` + +全局定义此宏可支持加载 Boost 1.84 之前版本保存至 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()); + +``` + +构造一个至少包含 `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_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` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原位构造] 到 `X` 容器的要求。 +返回;; 若插入成功,则返回类型的布尔分量为 true。 + ++ +若插入成功,则迭代器指向新插入的元素;否则,指向具有等价值的元素。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; 可能引发迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +指向元素的指针和引用永远不会失效。 + +--- + +==== emplace++_++hint +```c++ + +template iterator emplace_hint(const_iterator position, Args&&... args); + +``` + +当且仅当容器中不存在等价元素时,插入一个由参数 `args` 构造的对象。 + +`position` 是一个关于插入元素位置的建议。 + +[horizontal] +要求;; `value++_++type` 必须满足从 `args` 参数 https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[原位构造] 到 `X` 容器的要求。 +返回;; 若插入成功,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是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` 均为有效的成员类型别名,且 `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[可复制插入] 要求。 +返回;; 若插入成功,则迭代器指向新插入的元素;否则,指向具有等价键的元素。 +抛出;; 若异常由 `hasher` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是Boost.Unordered唯一支持的用法)是指向具有相同键的现有元素。 + ++ +可能引发迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +指向元素的指针和引用永远不会失效。 + ++ +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `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[原地构造] 到 `X` 中。 +抛出;; 若异常由 `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{plus}{plus}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{plus}{plus}17 标准中,通过此方法提取的节点可被插入到兼容的 `unordered++_++multiset` 中,但该特性暂未获得支持。 + ++ +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 类型隐式转换时, `template++<++class 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{plus}{plus}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` 调用之外的操作引发,则该函数不产生任何效果。 +注意;; C{plus}{plus}标准对提示参数含义的规定相当笼统。在实际使用中唯一有效的方式(也是Boost.Unordered唯一支持的用法)是指向具有相同键的现有元素。 + ++ +可能引发迭代器失效,但仅当插入操作导致负载因子大于或等于最大负载因子时才会发生。 + ++ +指向元素的指针和引用永远不会失效。 + ++ +此操作用于插入从兼容的 `unordered++_++multiset` 中提取的节点。 + +--- + +==== 通过位置擦除 + +```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` 抛出异常时,才会抛出异常。 +注意;; 仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名,且 `iterator` 和 `const++_++iterator` 均不能从 `K` 类型隐式转换时, `template++<++class 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++_++return++_++void +```c++ + +void erase_return_void(const_iterator position); + +``` + +擦除由 `position` 指向的元素。 + +[horizontal] +抛出;; 仅当 `hasher` 或 `key++_++equal` 抛出异常时,本函数才会抛出异常。 + ++ +在此实现中,此重载不会调用任一函数对象的方法,因此不会抛出异常,但在其他实现中可能并非如此。 +注意;; 此方法被实现是因为从擦除操作返回下一个元素的迭代器代价高昂,但容器经过重新设计后该问题已得到解决,因此该方法现已被弃用。 + +--- + +==== 交换 +```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{plus}{plus}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` 的成员函数模板。 + ++ +仅当 `Hash::is++_++transparent` 与 `Pred::is++_++transparent` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假定 `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` 均为有效的成员类型别名时, `template++<++class K++>++` 重载才会参与重载决议。标准库假设 `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++<++float++>++::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{plus}{plus}11 标准不完全一致,因为相等性谓词和哈希函数是通过其复制构造函数完成交换的。 + +--- + +=== erase++_++if +```c++ + +template + typename unordered_set::size_type + erase_if(unordered_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++_++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..99445db --- /dev/null +++ b/doc/modules/ROOT/pages/regular_zh_Hans.adoc @@ -0,0 +1,197 @@ +[#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<++` 、 `++>++=` +运算符进行比较。 +|可通过 `==` 与 `!=` 运算符进行比较。 + +| +|使用提示符插入时,实现允许忽略提示信息。 + +|=== + +--- + +[caption=",", title='Table {counter:table-counter} Complexity Guarantees'] +[cols="1,1,1", frame=all,, grid=rows] +|=== +|操作 |关联容器 |无序关联容器 + +|空容器的构造 +|常数时间 +|O(_n_),其中 _n_ 表示桶的最小数量。 + +|构建包含 _N_ 个元素范围的容器 +|O(_N log N_),若该范围按 +`value++_++comp()` 排序则为 O(_N_) +|平均 O(_N_),最差 O(_N^2^_) + +|插入单个元素 +|对数时间 +|平均情况常数时间,最坏情况线性时间 + +|使用提示插入单个元素 +|若在提示后立即插入 `t` +个元素,则均摊常数时间,否为则对数时间 +|平均情况常数时间,最坏情况线性时间(与常规插入操作相同)。 + +|插入包含 _N_ 个元素的范围 +|_N_ log(`size()` {plus} _N_) +|平均情况 +O(_N_),最坏情况 O(_N_ ++*++ `size()`) + +|按键 `k` 删除 +|O(log(`size()`) {plus} `count(k)`) +|平均情况:O(`count(k)`),最差情况:O(`size()`) + +|通过迭代器删除单个元素 +|均摊常数时间 +|平均情况:O(1),最差情况:O(`size()`) + +|删除包含 _N_ 个元素的范围 +|O(log(`size()`) {plus} _N_) +|平均情况:O(_N_),最差情况:O(`size()`) + +|清空容器 +|O(`size()`) +|O(`size()`) + +|查找 +|对数时间 +|平均情况:O(1),最差情况:O(`size()`) + +|统计 +|O(log(`size()`) {plus} `count(k)`) +|平均情况:O(1),最差情况:O(`size()`) + +|`equal++_++range(k)` +|对数时间 +|平均情况:O(`count(k)`),最差情况:O(`size()`) + +|`lower++_++bound` 、 `upper++_++bound` +|对数时间 +|不适用 + +|=== 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..15fd66e --- /dev/null +++ b/doc/modules/ROOT/pages/structures_zh_Hans.adoc @@ -0,0 +1,133 @@ +[#structures] += 数据结构 + +:idprefix: structures_ + +== 闭寻址容器 + +++++ + +++++ + +Boost.Unordered 提供了速度最快的闭寻址实现之一,该结构通常被称为 https://en.wikipedia.org/wiki/Hash_table#Separate_chaining[分离链接法] 。其数据结构示例如下: + +[#img-bucket-groups, .text-center] +.基础桶组实现方案 +image::bucket-groups.png[align=center] + +系统分配一个"桶"数组,其中每个桶分别指向其专属的链表。这种结构使得桶迭代能直接满足标准要求。但采用此布局进行整体容器迭代通常较慢,因为需检查每个桶的占用状态,其时间复杂度为 `O(bucket++_++count() {plus} size())` ,而标准要求复杂度应为 `O(size())` 。 + +典型的标准库实现最终会形成如下图所示的结构: + +[.text-center] +.典型标准库实现结构 +image::singly-linked.png[align=center,link=_images/singly-linked.png,window=_blank] + +值得注意的是,此方案仅被libc{plus}{plus}和libstdc{plus}{plus}采用;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]; // first load +if (n) { + value_type const& v = *n; // second load + // ... +} + +``` + +实际上,这种额外的间接访问会对常见操作(如 `insert` 、 `find` 和 `erase` )产生显著的性能影响。但为了保持容器迭代的高效性,Boost.Unordered 引入了一种新颖的数据结构——“桶组”。桶组是桶数组某个分区的定宽视图,它包含一个位掩码( `std::size++_++t` 类型)用于跟踪桶的占用状态,并包含两个指针以形成非空组之间的双向链表。其结构示例如下: + +[#img-fca-layout] +.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] +.Boost.Unordered采用的开放寻址布局结构. +image::foa.png[align=center] + +与所有开放寻址容器一样,元素(对于 `boost::unordered++_++node++_++set` 和 `boost::unordered++_++node++_++map`则是元素节点的指针)直接存储在桶数组中。 该数组在逻辑上被划分为 2^_n_^个组,每组包含 15个元素。 除桶数组外,还存在一个关联的__元数据数组__,其中包含 2^_n_^ 个 16 字节的字。 + +[#img-foa-metadata] +.元数据字结构解析。 +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指令快速扫描整个元数据字: 不匹配桶立即排除, 缩减哈希值匹配的桶需与对应元素进行完整比对。 若未找到目标元素, 则检查溢出字节: + +- 若位置 _h_ mod 8 的比特位为零,则查找终止(确认元素不存在)。 元素不存在)。 +- 若该比特位为1(表示该组已__溢出__),则通过 https://en.wikipedia.org/wiki/Quadratic_probing[_二次探查法_] 检查后续组,并重复此过程。 + +插入操作的算法逻辑类似:通过 SIMD 技术定位空桶,当遍历到已满的组时,会将其对应的溢出位设置为 1。 + +在不支持 SIMD 的体系架构中, 逻辑布局不变,但采用__位交织__技术编码元数据字: 仅用标准算术和逻辑运算即可模拟接近SIMD的性能. + +[#img-foa-metadata-interleaving] +.位交织元数据字结构. +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] +.Boost.Unordered采用的并发开放寻址布局结构. +image::cfoa.png[align=center] + +采用两级同步机制: + +* 容器级:读写互斥锁控制所有操作访问。 通常情况下,即使是修改操作也采用读模式访问(即可并发执行), 因此在大多数实际应用场景中,该层级不会出现线程争用。 仅当执行重哈希或容器级操作(如交换或赋值)时, 才会采用写模式访问(即阻塞式)。 +* 组级:每个包含15个槽位的组都配备一个8字节的字,其中包含: + ** 一个用于同步访问组内任意元素的读写自旋锁. + ** 用于乐观插入的原子__插入计数器__ + (如下所述). + +通过原子操作访问组元数据, 查找在(组级别上)无锁状态下进行, 直至需对先前经SIMD匹配的元素进行实际比较时,才使用组自旋锁. + +插入操作采用如下__乐观算法__: + +* 在探查序列的初始组中, 插入计数器的值会被本地记录(我们称此值为 `c0` )。 +* 查找操作按上述方式执行。 若查找未发现相等元素, 则会在探查序列中依次锁定/解锁各个分组来搜索可用插槽。 +* 当定位到可用插槽时, 会抢先占用该插槽(设置其缩减哈希值)并原子地递增插入计数器: 若在整个操作期间没有其他线程递增过该计数器 (通过与 `c0` 比对确认), 则操作成功并完成插入; 否则回滚并重新开始。 + +该算法在查找和实际插入阶段都具有极低的争用, 其代价是:当其他线程从同一初始组开始执行插入操作并成功时, 当前线程的计算可能需要重新执行。 实际应用中, 重启频率极低( 部分基准测试中为百万分之一量级). + +如需了解有关实现原理的更多信息,请阅读 xref:rationale.adoc#rationale_concurrent_containers[相关说明章节] 。