引言
哈希表 是一种极其流行的计算机数据结构,几乎可以在任何编程语言中以某种形式找到。而其他关联结构,如 rb-树(在 C++ 中由std::set
和std::map
使用)对插入和查找具有对数时间复杂度,如果配置正确,哈希表平均情况下可以以常数时间执行这些操作,并且通常速度要快得多。
C++ 在 C++11 中引入了 *无序关联容器* std::unordered_set
、std::unordered_map
、std::unordered_multiset
和 std::unordered_multimap
,但哈希表的研 究并没有停止:CPU 架构的进步,例如更强大的缓存、SIMD 操作和越来越多的多核处理器 ,为改进的基于哈希的数据结构和新的用例开辟了可能性,这些用例根本无法通过 2011 年指定的无序关联容器实现。
Boost.Unordered 提供了一个具有不同标准符合性级别、性能和预期使用场景的哈希容器目录。
基于节点的 |
扁平的 |
|
---|---|---|
闭址 |
|
|
开址 |
|
|
并发 |
|
|
-
**闭址容器** 完全符合 C++ 无序关联容器的规范,并在所需标准接口施加的技术约束内具有市场上最快的实现之一。
-
**开址容器** 依赖于快得多的数据结构和算法(在典型情况下快两倍以上),同时略微偏离标准接口以适应实现。有两种变体:**扁平的**(最快的)和**基于节点的**,它们在重新哈希时提供指针稳定性,但代价是速度较慢。
-
最后,**并发容器** 旨在用于高性能多线程场景。它们的接口与常规 C++ 容器的接口截然不同。提供了扁平的和基于节点的变体。
Boost.Unordered 中的所有集合和映射的实例化方式与std::unordered_set
和std::unordered_map
分别类似。
namespace boost {
template <
class Key,
class Hash = boost::hash<Key>,
class Pred = std::equal_to<Key>,
class Alloc = std::allocator<Key> >
class unordered_set;
// same for unordered_multiset, unordered_flat_set, unordered_node_set,
// concurrent_flat_set and concurrent_node_set
template <
class Key, class Mapped,
class Hash = boost::hash<Key>,
class Pred = std::equal_to<Key>,
class Alloc = std::allocator<std::pair<Key const, Mapped> > >
class unordered_map;
// same for unordered_multimap, unordered_flat_map, unordered_node_map,
// concurrent_flat_map and concurrent_node_map
}
将对象存储在无序关联容器中需要键相等函数和哈希函数。标准容器中的默认函数对象支持一些基本类型,包括整数类型、浮点类型、指针类型和标准字符串。由于 Boost.Unordered 使用boost::hash,它还支持其他一些类型,包括标准容器。要使用这些方法不支持的任何类型,您必须扩展 Boost.Hash 以支持该类型,或者使用您自己的自定义相等谓词和哈希函数。有关更多详细信息,请参阅相等谓词和哈希函数 部分。
哈希表基础
容器由多个 *桶* 组成,每个桶可以包含任意数量的元素。例如,下图显示了一个具有 7 个桶并包含 5 个元素A
、B
、C
、D
和E
的boost::unordered_set
(这只用于说明,容器通常会有更多桶)。
为了确定将元素放置在哪个桶中,容器将哈希函数Hash
应用于元素的键(对于集合,键是整个元素,但称为键以便可以对集合和映射使用相同的术语)。这将返回一个std::size_t
类型的数值。std::size_t
的数值范围远大于桶的数量,因此容器将另一个转换应用于该值以选择要将元素放置在其中的桶。
检索给定键的元素很简单。相同的过程应用于键以查找正确的桶。然后将键与桶中的元素进行比较以查找任何匹配的元素(使用相等谓词Pred
)。如果哈希函数运行良好,则元素将均匀地分布在各个桶中,因此只需要检查少量元素。
您可以从图中看到A
和D
已被放置在同一个桶中。在查找此桶中的元素时,最多会进行 2 次比较,从而使搜索速度变慢。这称为**冲突**。为了保持速度,我们尽量将冲突降到最低。
如果我们使用boost::unordered_flat_set
而不是boost::unordered_set
,则该图将如下所示。
在开址容器中,桶最多只能容纳一个元素;如果发生冲突(例如示例中的D
),则该元素将使用原始位置附近的一些其他可用桶。鉴于这种情况更简单,Boost.Unordered 开址容器提供了非常有限的 API 来访问桶。
所有容器 |
|
---|---|
方法 |
描述 |
|
桶的数量。 |
仅闭址容器 |
|
方法 |
描述 |
|
桶数量的上限。 |
|
桶 |
|
返回将包含 |
|
返回桶 |
|
|
|
|
|
|
|
|
|
控制桶的数量
随着无序关联容器中添加的元素越来越多,冲突的数量也会增加,导致性能下降。为了解决这个问题,容器会在插入元素时增加桶的数量。您也可以通过调用rehash
来告诉容器(如果需要)更改桶的数量。
标准为实现者决定桶的数量选择方式留下了很大的自由度,但它确实根据容器的负载因子(元素数量除以桶的数量)提出了一些要求。容器还具有一个最大负载因子,它们应该尽量保持负载因子低于此值。
您无法直接控制桶的数量,但有两种方法可以影响它
-
在构造容器或调用
rehash
时指定最小桶数。 -
通过调用
max_load_factor
建议最大负载因子。
max_load_factor
并不能让您自己设置最大负载因子,它只是让您给出一个提示。即使那样,标准实际上也不要求容器过多地关注此值。负载因子*必须*小于最大值的情况只有在调用rehash
之后。但是大多数实现都会尝试将元素数量保持在最大负载因子以下,并将最大负载因子设置为与提示相同或接近的值——除非您的提示不合理地小或大。
所有容器 |
|
---|---|
方法 |
描述 |
|
构造一个至少具有 |
|
构造一个至少具有 |
|
每个桶的平均元素数量。 |
|
返回当前最大负载因子。 |
|
使用 |
|
更改桶的数量,以便至少有 |
仅限开放寻址和并发容器 |
|
方法 |
描述 |
|
返回容器在重新哈希之前允许的最大元素数量。 |
关于开放寻址和并发容器的max_load
说明:在rehash
之后或容器创建时,最大负载将为(max_load_factor() * bucket_count()
),但在高负载情况下删除元素时可能会略微减少。例如,如果我们有一个boost::unordered_flat_map
,其size()
几乎达到max_load()
级别,然后删除1000个元素,max_load()
可能会减少大约几十个元素。Boost.Unordered内部执行此操作是为了保持其性能稳定,在规划无重新哈希插入时必须考虑这一点。
相等谓词和哈希函数
关联容器使用排序关系来指定元素的存储方式,而无序关联容器使用相等谓词和哈希函数。例如,boost::unordered_map声明为
template <
class Key, class Mapped,
class Hash = boost::hash<Key>,
class Pred = std::equal_to<Key>,
class Alloc = std::allocator<std::pair<Key const, Mapped> > >
class unordered_map;
哈希函数排在前面,因为您可能希望更改哈希函数而不是相等谓词。例如,如果您想使用FNV-1a哈希函数,您可以编写
boost::unordered_map<std::string, int, hash::fnv_1a>
dictionary;
示例目录中有一个FNV-1a的实现。
如果您希望使用不同的相等函数,您还需要使用匹配的哈希函数。例如,要实现不区分大小写的字典,您需要定义不区分大小写的相等谓词和哈希函数
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;
}
};
然后,您可以在不区分大小写的字典中使用它
boost::unordered_map<std::string, int, ihash, iequal_to>
idictionary;
这是/libs/unordered/examples/case_insensitive.hpp中示例的简化版本,该示例支持其他语言环境和字符串类型。
警告
|
使用自定义相等谓词时,尤其是在使用函数指针时,请小心使用相等(== )运算符。如果使用不同的相等谓词比较两个容器,则结果未定义。对于大多数无状态函数对象来说,这是不可能的——因为您只能比较具有相同相等谓词的对象,所以您知道相等谓词必须相等。但是,如果您使用函数指针或有状态相等谓词(例如boost::function ),则可能会遇到麻烦。 |
自定义类型
类似地,自定义哈希函数可用于自定义类型
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<point, point_hash> points;
由于默认哈希函数是Boost.Hash,我们可以扩展它以支持该类型,以便无需显式给出哈希函数
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<point> points;
有关如何执行此操作的更多详细信息,请参阅Boost.Hash文档。请记住,它依赖于对标准的扩展——因此它不适用于无序关联容器的其他实现,您需要显式使用Boost.Hash。
方法 | 描述 |
---|---|
|
返回容器的哈希函数。 |
|
返回容器的关键相等函数。 |
常规容器
Boost.Unordered闭址容器(boost::unordered_set
、boost::unordered_map
、boost::unordered_multiset
和boost::unordered_multimap
)完全符合C++无序关联容器规范,因此对于那些知道如何使用std::unordered_set
、std::unordered_map
等的使用者来说,它们在Boost.Unordered中的同名词可以直接替换。开放寻址容器(boost::unordered_node_set
、boost::unordered_node_map
、boost::unordered_flat_set
和boost::unordered_flat_map
)的接口非常相似,但它们存在一些在专用标准符合性部分中列出的细微差别。
对于没有使用哈希容器的经验但熟悉普通关联容器(std::set
、std::map
、std::multiset
和std::multimap
)的读者,Boost.Unordered容器的使用方式类似
typedef boost::unordered_map<std::string, int> map;
map x;
x["one"] = 1;
x["two"] = 2;
x["three"] = 3;
assert(x.at("one") == 1);
assert(x.find("missing") == x.end());
但是,由于元素未排序,因此
for(const map::value_type& i: x) {
std::cout<<i.first<<","<<i.second<<"\n";
}
的输出可以是任意顺序。例如,它可能是
two,2
one,1
three,3
还有其他差异,这些差异列在与关联容器的比较部分。
迭代器失效
未指定除rehash
和reserve
之外的其他成员函数如何影响桶计数,尽管只有当插入导致容器的负载大于最大允许负载时,insert
才会使迭代器失效。对于大多数实现,这意味着insert
只会在发生这种情况时才会更改桶的数量。迭代器可能会被对insert
、rehash
和reserve
的调用使无效。
至于指针和引用,它们对于基于节点的容器(boost::unordered_[multi]set
、boost::unordered_[multi]map
、boost::unordered_node_set
、boost::unordered_node_map
)永远不会失效,但是当对boost::unordered_flat_set
和boost::unordered_flat_map
进行重新哈希时,它们会失效:这是因为这些容器将元素直接存储到它们的持有桶中,因此在分配新的桶数组时,必须通过移动构造来转移元素。
与对vector
使用reserve
类似,在插入大量元素之前调用reserve
可能是一个好主意。这将消除代价高昂的重新哈希操作,并让您可以安全地存储迭代器,确信它们不会失效。如果您要将n
个元素插入容器x
中,您可以首先调用
x.reserve(n);
- 注意
-
reserve(n)
预留至少n
个元素的空间,分配足够的桶以不超过最大负载因子。因为最大负载因子定义为元素数量除以可用桶的总数,所以此函数在逻辑上等效于
x.rehash(std::ceil(n / x.max_load_factor()))
有关
rehash
函数的更多详细信息,请参阅参考。
与关联容器的比较
关联容器 | 无序关联容器 |
---|---|
由排序关系 |
由函数对象 |
可以使用成员函数 |
可以使用成员函数 |
构造函数具有用于比较对象的可选额外参数。 |
构造函数具有用于初始最小桶数、哈希函数和相等对象的可选额外参数。 |
如果 |
如果 |
成员函数 |
没有等价项。由于元素未排序,因此 |
|
|
|
|
容器元素的迭代器、指针和引用永远不会失效。 |
迭代器可能会因调用插入或重新哈希操作而失效。. |
迭代器按照比较对象定义的顺序遍历容器。 |
迭代器以任意顺序遍历容器,该顺序可能会随着元素的插入而改变,尽管等效元素始终相邻。 |
无等效项 |
闭地址容器:可以使用局部迭代器遍历各个桶。(局部迭代器和迭代器的顺序不需要有任何对应关系。) |
可以使用 |
可以使用 |
当使用提示进行插入时,允许实现忽略该提示。 |
操作 | 关联容器 | 无序关联容器 |
---|---|---|
空容器的构造 |
常数时间 |
O(n),其中n是最小桶数。 |
从N个元素的范围构造容器 |
O(N log N),如果范围已按 |
平均情况 O(N),最坏情况 O(N2) |
插入单个元素 |
对数时间 |
平均情况为常数时间,最坏情况为线性时间 |
使用提示插入单个元素 |
如果在提示之后立即插入 |
平均情况为常数时间,最坏情况为线性时间(即与普通插入相同)。 |
插入N个元素的范围 |
N log( |
平均情况 O(N),最坏情况 O(N * |
按键删除, |
O(log( |
平均情况:O( |
按迭代器删除单个元素 |
均摊常数时间 |
平均情况:O(1),最坏情况:O( |
删除N个元素的范围 |
O(log( |
平均情况:O(N),最坏情况:O( |
清空容器 |
O( |
O( |
查找 |
对数时间 |
平均情况:O(1),最坏情况:O( |
计数 |
O(log( |
平均情况:O(1),最坏情况:O( |
|
对数时间 |
平均情况:O( |
|
对数时间 |
n/a |
并发容器
Boost.Unordered 提供了boost::concurrent_node_set
、boost::concurrent_node_map
、boost::concurrent_flat_set
和boost::concurrent_flat_map
,这些哈希表允许来自不同线程的并发读写访问,而无需用户实现任何同步机制。
std::vector<int> input;
boost::concurrent_flat_map<int,int> m;
...
// process input in parallel
const int num_threads = 8;
std::vector<std::jthread> threads;
std::size_t chunk = input.size() / num_threads; // how many elements per thread
for (int i = 0; i < num_threads; ++i) {
threads.emplace_back([&,i] {
// calculate the portion of input this thread takes care of
std::size_t start = i * chunk;
std::size_t end = (i == num_threads - 1)? input.size(): (i + 1) * chunk;
for (std::size_t n = start; n < end; ++n) {
m.emplace(input[n], calculation(input[n]));
}
});
}
在上面的示例中,线程访问m
无需同步,就像在单线程场景中一样。在理想情况下,如果给定的工作负载分布在N个线程之间,则执行速度比使用一个线程快N倍——由于同步开销和竞争(一个线程等待另一个线程离开地图的锁定部分),在实践中永远无法达到此限制,但 Boost.Unordered 并发容器的设计开销非常小,通常可以实现线性扩展(即性能与线程数成正比,直到 CPU 的逻辑核心数)。
基于访问的API
Boost.Unordered 并发容器的新用户首先会注意到,这些类不提供迭代器(这在技术上使它们不是C++标准意义上的容器)。原因是迭代器本质上是线程不安全的。考虑以下假设代码
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
也未提供。相反,元素访问是通过所谓的访问函数完成的
m.visit(k, [](const auto& x) { // x is the element with key k (if it exists)
some_function(x); // use it
});
用户传入的访问函数(在本例中为 lambda 函数)由 Boost.Unordered 以线程安全的方式内部执行,因此它可以访问元素而无需担心其他线程干扰该过程。
另一方面,访问函数不能访问容器本身
m.visit(k, [&](const auto& x) {
some_function(x, m.size()); // forbidden: m can't be accessed inside visitation
});
不过,允许访问不同的容器
m.visit(k, [&](const auto& x) {
if (some_function(x)) {
m2.insert(x); // OK, m2 is a different boost::concurrent_flat_map
}
});
但是,一般来说,访问函数应尽可能轻量级,以减少竞争并提高并行化。在某些情况下,将繁重的工作移到访问之外可能会有益。
std::optional<value_type> o;
bool found = m.visit(k, [&](const auto& x) {
o = x;
});
if (found) {
some_heavy_duty_function(*o);
}
访问在并发容器提供的 API 中很突出,许多经典操作都有启用访问的变体。
m.insert_or_visit(x, [](auto& y) {
// if insertion failed because of an equivalent element y,
// do something with it, for instance:
++y.second; // increment the mapped part of the element
});
请注意,在这个最后的例子中,访问函数实际上可以修改元素:作为一般规则,对并发映射m
的操作将根据m
是常量/非常量来向访问函数授予对元素的常量/非常量访问权限。可以使用cvisit
重载(例如,insert_or_cvisit
)始终显式请求常量访问,这可能会导致更高的并行化。另一方面,对于并发集合,访问始终是常量访问。
尽管预计使用频率要低得多,但并发容器还提供插入操作,其中可以在元素创建后立即访问元素(除了元素已经存在时通常的访问之外)。
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";
});
请参阅boost::concurrent_node_set
、boost::concurrent_node_map
、boost::concurrent_flat_set
和boost::concurrent_flat_map
的参考资料,了解启用访问操作的完整列表。
全表访问
在没有迭代器的情况下,提供visit_all
作为处理容器中所有元素的替代方法。
m.visit_all([](auto& x) {
x.second = 0; // reset the mapped part of the element
});
在实现标准并行算法的 C++17 编译器中,可以并行化整个表访问。
m.visit_all(std::execution::par, [](auto& x) { // run in parallel
x.second = 0; // reset the mapped part of the element
});
可以中途中断遍历。
// finds the key to a given (unique) value
int key = 0;
int value = ...;
bool found = !m.visit_while([&](const auto& x) {
if(x.second == value) {
key = x.first;
return false; // finish
}
else {
return true; // keep on visiting
}
});
if(found) { ... }
还有一个最后的整个表访问操作,erase_if
。
m.erase_if([](auto& x) {
return x.second == 0; // erase the elements whose mapped value is zero
});
visit_while
和erase_if
也可以并行化。请注意,为了提高效率,整个表访问操作在执行期间不会阻塞表:这意味着在访问期间,其他线程可能会插入、修改或删除元素。建议不要对程序中任何时间点并发容器的精确全局状态做太多假设。
批量访问
假设您有一个要在一个并发映射中查找的键的std::array
。
std::array<int, N> keys;
...
for(const auto& key: keys) {
m.visit(key, [](auto& x) { ++x.second; });
}
批量访问允许我们一次性传递所有键。
m.visit(keys.begin(), keys.end(), [](auto& x) { ++x.second; });
但是,此功能并非仅仅为了语法上的方便而提供:通过一次处理所有键,可以应用一些内部优化来提高与常规的一次一个案例相比的性能(请参阅基准测试)。实际上,缓冲传入的键可能是有益的,以便可以将其分块进行批量访问。
static constexpr auto bulk_visit_size = boost::concurrent_flat_map<int,int>::bulk_visit_size;
std::array<int, bulk_visit_size> buffer;
std::size_t i=0;
while(...) { // processing loop
...
buffer[i++] = k;
if(i == bulk_visit_size) {
map.visit(buffer.begin(), buffer.end(), [](auto& x) { ++x.second; });
i = 0;
}
...
}
// flush remaining keys
map.visit(buffer.begin(), buffer.begin() + i, [](auto& x) { ++x.second; });
这里存在延迟/吞吐量权衡:处理传入键需要更长时间(因为它们被缓冲),但每秒处理的键数更多。bulk_visit_size
是推荐的块大小——较小的缓冲区可能会导致性能下降。
阻塞操作
并发容器可以像任何其他 Boost.Unordered 容器一样进行复制、赋值、清除和合并。与大多数其他操作不同,这些操作是阻塞的,也就是说,在复制、赋值、清除或合并操作正在进行时,所有其他线程都被阻止访问所涉及的表。阻塞由库自动处理,用户无需采取任何特殊预防措施,但整体性能可能会受到影响。
另一个阻塞操作是重新哈希,它通过rehash
/reserve
显式发生,或者在表的负载达到max_load()
时在插入期间发生。与非并发容器一样,预先为批量插入保留空间通常会加快处理速度。
与非并发容器的互操作性
由于开地址和并发容器基于相同的数据结构,因此可以从它们的非并发对应物高效地移动构造,反之亦然。
|
|
|
|
|
|
|
|
这种互操作性在多阶段场景中非常方便,在这些场景中,数据处理的部分工作并行进行,而其他步骤是非并发(或非修改)的。在下面的示例中,我们希望根据单词的巨大输入向量构建直方图:可以使用boost::concurrent_flat_map
并行执行填充阶段,然后将结果传输到最终容器。
std::vector<std::string> words = ...;
// Insert words in parallel
boost::concurrent_flat_map<std::string_view, std::size_t> m0;
std::for_each(
std::execution::par, words.begin(), words.end(),
[&](const auto& word) {
m0.try_emplace_or_visit(word, 1, [](auto& x) { ++x.second; });
});
// Transfer to a regular unordered_flat_map
boost::unordered_flat_map m=std::move(m0);
哈希质量
为了正常工作,哈希表要求提供的哈希函数具有良好的质量,大致意味着它尽可能均匀地使用其std::size_t
输出空间,就像随机数生成器一样——当然,哈希函数的值不是随机的,而是严格由其输入参数决定的。
Boost.Unordered 中的闭地址容器对质量欠佳的哈希函数相当稳健,但开地址和并发容器对该因素更为敏感,如果哈希函数不合适,它们的性能可能会急剧下降。一般来说,如果您使用由Boost.Hash提供的或使用Boost.Hash生成的函数,则质量将足够,但在使用替代哈希算法时必须小心。
本节的其余部分仅适用于开地址和并发容器。
哈希后混淆和雪崩特性
即使您提供的哈希函数不符合开地址要求的均匀行为,Boost.Unordered 容器的性能也可能可以接受,因为库执行内部后混合步骤以改进计算的哈希值的统计属性。这会带来额外的计算成本;如果您想选择退出后混合,请如下注释您的哈希函数:
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
{
...
}
};
通过设置hash_is_avalanching特性,我们告知 Boost.Unordered my_string_hash_function
的质量足以直接使用,无需任何后混合安全网。这样做会冒在哈希函数行为不如我们声明的那样好的情况下性能下降的风险。
容器统计信息
如果我们在全局定义宏BOOST_UNORDERED_ENABLE_STATS
,开地址和并发容器将计算一些与哈希函数质量直接相关的内部统计数据。
#define BOOST_UNORDERED_ENABLE_STATS
#include <boost/unordered/unordered_map.hpp>
...
int main()
{
boost::unordered_flat_map<std::string, int, my_string_hash> m;
... // use m
auto stats = m.get_stats();
... // inspect stats
}
stats
对象提供以下信息:
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
维护三个内部操作的统计数据:插入(不考虑先前查找以确定键尚不存在)、成功查找和不成功查找(包括插入元素时内部发出的查找)。探测长度是每个操作访问的桶组数。如果哈希函数的行为正确
-
平均探测长度应接近 1.0。
-
每次成功查找的平均比较次数应接近 1.0(即,只检查找到的元素)。
-
每次不成功查找的平均比较次数应接近 0.0。
这里提供了一个示例,用于显示boost::hash<std::string>
的容器统计信息,该实现使用了FNV-1a 哈希算法,以及两个行为不良的自定义哈希函数(这些函数被错误地标记为雪崩效应)。
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
标准符合性
闭址容器
boost::unordered_[multi]set
和boost::unordered_[multi]map
为 C++11(或更高版本)编译器提供了符合最新 C++ 无序关联容器标准修订版的实现,仅存在一些非常小的偏差(已注明)。这些容器完全支持分配器,并支持高级指针。
推导指南
仅在 C++17(或更高版本)编译器上提供类模板参数推导 (CTAD) 的推导指南。
分段对插入
根据标准规范,boost::unordered_[multi]map::emplace
支持分段对构造。
boost::unordered_multimap<std::string, std::complex> x;
x.emplace(
std::piecewise_construct,
std::make_tuple("key"), std::make_tuple(1, 2));
此外,还通过非标准的boost::unordered::piecewise_construct
和 Boost.Tuple 提供了相同的功能。
x.emplace(
boost::unordered::piecewise_construct,
boost::make_tuple("key"), boost::make_tuple(1, 2));
保留此功能是为了向后兼容以前的 Boost.Unordered 版本:鼓励用户更新其代码以改用std::piecewise_construct
和std::tuple
。
交换
在交换时,目前不会通过调用swap
来交换Pred
和Hash
,而是使用它们的复制构造函数。因此,在交换时,它们的复制构造函数可能会抛出异常。
开址容器
C++ 标准目前没有提供任何开放寻址容器规范,因此boost::unordered_flat_set
/unordered_node_set
和boost::unordered_flat_map
/unordered_node_map
分别从std::unordered_set
和std::unordered_map
中汲取灵感,并在方便时或根据其内部数据结构的要求偏离其接口,这与标准(闭合寻址)强加的数据结构有根本的不同。
与 C++ 无序关联容器的主要区别在于
-
一般来说
-
begin()
不是常数时间。 -
erase(iterator)
不会返回指向下一个元素的迭代器,而是一个代理对象,如果需要,该对象会转换为该迭代器;这避免了在不需要时进行潜在代价高昂的迭代器递增操作。 -
没有用于桶处理的 API(
bucket_count
除外)。 -
容器的最大负载因子是在内部管理的,用户无法设置。通过公共函数
max_load
公开的最大负载可能会在高负载条件下的擦除操作中减小。
-
-
扁平容器 (
boost::unordered_flat_set
和boost::unordered_flat_map
)-
value_type
必须是可移动构造的。 -
在重新哈希时,不会保持指针稳定性。
-
没有用于节点提取/插入的 API。
-
并发容器
C++ 标准目前没有针对此类或任何其他类型的并发数据结构的规范。boost::concurrent_flat_set
/boost::concurrent_node_set
和boost::concurrent_flat_map
/boost::concurrent_node_map
的 API 分别模拟了std::unordered_flat_set
和std::unordered_flat_map
,关键区别在于由于迭代器在并发场景中的固有问题(高争用,易于死锁),因此不提供迭代器:因此,Boost.Unordered 并发容器在技术上不是容器的模型,尽管它们满足支持分配器容器的所有要求(包括高级指针支持),除了那些暗示迭代器的要求。
在非并发无序容器中,迭代器主要有两个用途:
-
访问之前通过查找定位的元素。
-
容器遍历。
Boost.Unordered 并发容器用内部访问机制代替迭代器,作为线程安全的替代方案。
iterator find(const key_type& k);
std::pair<iterator, bool> insert(const value_type& obj);
例如,返回指向容器中已存在元素的迭代器的经典操作会被转换为接受一个访问函数,该函数会传递这样的元素。
template<class F> size_t visit(const key_type& k, F f);
template<class F> bool insert_or_visit(const value_type& obj, F f);
(在第二种情况下,只有当表中存在与obj
等效的元素时,才会调用f
,如果插入成功则不会调用)。容器遍历由以下内容提供:
template<class F> size_t visit_all(F f);
在支持并行算法的 C++17 编译器中,这些算法有并行版本。通常,并发容器的接口是通过一个相当简单的过程从其非并发对应物的接口派生的,该过程在适用时将迭代器替换为访问。如果对于常规映射,iterator
和const_iterator
分别提供对元素的可变和常量访问,则此处访问会根据所用成员函数的常量性授予可变或常量访问(还有一些*cvisit
重载用于显式常量访问);在boost::concurrent_flat_set
的情况下,访问始终是常量。
boost::concurrent_flat_map
/boost::concurrent_node_map
没有提供的值得注意的操作是operator[]
/at
,如果以更复杂的方式,可以使用try_emplace_or_visit
替换。
数据结构
闭址容器
Boost.Unordered 拥有闭合寻址(也通常称为分离链接)最快实现之一。下图显示了数据结构的示例。
分配一个“桶”数组,每个桶又指向其自己的单独链表。这使得满足桶迭代的标准要求变得简单明了。不幸的是,使用此布局,整个容器的迭代通常很慢,因为必须检查每个桶的占用情况,当标准要求复杂度为O(size())
时,其时间复杂度为O(bucket_count() + size())
。
规范的标准实现最终看起来像下面的图表:
值得注意的是,这种方法仅由 libc++ 和 libstdc++ 使用;MSVC Dinkumware 实现使用不同的方法。可以在此处找到对标准容器的更详细分析。
选择这种不同寻常的布局是为了使整个容器的迭代更高效,方法是将所有节点连接到一个单链表中。还可以注意到,桶指向桶元素之前的节点。这样做是为了高效地从列表中删除元素,而无需引入双链表。不幸的是,这种数据结构引入了额外的间接寻址。
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
// ...
}
使用简单的桶组布局,只需执行以下操作:
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
),用于跟踪桶的占用情况,并包含两个指针,以便它可以与非空组形成双链表。下图是一个示例图表。
因此,容器范围的迭代变成了遍历非空桶组(一个具有常数时间复杂度的操作),这将时间复杂度降低回O(size())
。总的来说,桶组的大小只有 4 个字,它查看sizeof(std::size_t) * CHAR_BIT
个桶,这意味着对于所有常见的实现,桶组只引入了每个桶 4 位的空间开销。
开址容器
该图显示了boost::unordered_flat_set
/unordered_node_set
和boost:unordered_flat_map
/unordered_node_map
的基本内部布局。
与所有开放寻址容器一样,元素(或者在boost::unordered_node_set
和boost::unordered_node_map
的情况下为指向元素节点的指针)直接存储在桶数组中。该数组在逻辑上被划分为 2n 个组,每个组包含 15 个元素。除了桶数组外,还有一个关联的元数据数组,其中包含 2n 个 16 字节的字。
元数据字被分成 15 个hi字节(每个关联的桶一个)和一个溢出字节(图中的ofw)。hi的值为:
-
如果相应的桶为空,则为 0。
-
1 用于编码一个称为哨兵的特殊空桶,该桶用于在完全遍历容器时在内部停止迭代。
-
如果桶已占用,则为从元素的哈希值获得的简化哈希值。
在查找哈希值为h的元素时,SIMD技术,例如SSE2和Neon,允许我们非常快速地检查完整的元数据字,并在所有15个桶中查找h的简化值,只需少量CPU指令:不匹配的桶可以很容易地丢弃,而简化哈希值匹配的桶则需要通过与相应元素进行完整比较来检查。如果找不到要查找的元素,则检查溢出字节。
-
如果位置h mod 8中的位为零,则查找终止(并且不存在该元素)。
-
如果位设置为1(该组已溢出),则使用二次探测检查其他组,并重复此过程。
插入算法类似:使用SIMD查找空桶,当超过完整组时,其对应的溢出位设置为1。
在不支持SIMD的架构中,逻辑布局保持不变,但元数据字使用我们称之为位交错的技术进行编码:这种布局允许我们仅使用标准算术和逻辑运算就能以相当好的性能模拟SIMD。
并发容器
boost::concurrent_flat_set
/boost::concurrent_node_set
和boost::concurrent_flat_map
/boost::concurrent_node_map
使用上面描述的基本开地址布局,并增加了同步机制。
使用了两个级别的同步
-
容器级别:读写互斥锁用于控制任何操作对容器的访问。通常,即使对于修改操作,此类访问也处于读取模式(即并发),因此对于大多数实际目的,此级别没有线程争用。只有在重新哈希或执行容器范围的操作(例如交换或赋值)时,访问才处于写入模式(阻塞)。
-
组级别:每个15槽组都配备了一个8字节字,包含:
-
一个读写自旋锁,用于同步访问组中的任何元素。
-
一个原子插入计数器,用于如下所述的乐观插入。
-
通过使用原子操作访问组元数据,查找是(组级别)无锁的,直到需要与先前SIMD匹配的元素进行实际比较:只有那时才使用组的自旋锁。
插入使用以下乐观算法
-
记录探测序列中初始组的插入计数器值(我们将其称为
c0
)。 -
查找如上所述。如果查找未找到等效元素,则搜索可用插槽以进行插入,依次锁定/解锁探测序列中的每个组。
-
找到可用插槽后,会抢占式占用它(设置其简化哈希值),并原子递增插入计数器:如果在整个操作期间没有其他线程递增计数器(通过与
c0
比较进行检查),则可以继续并完成插入,否则回滚并重新开始。
这种算法在查找和实际插入阶段的争用非常低,但如果其他线程通过从同一组开始执行成功的插入而干扰该过程,则计算可能需要重新开始。在实践中,重新开始的频率非常小,根据我们的一些基准测试,其测量范围为百万分之几。
有关实现原理的更多信息,请阅读相应章节。
可调试性
Visual Studio Natvis
所有容器和迭代器在Natvis框架中都有自定义可视化。
在你的项目中使用
要在项目中的Natvis框架中可视化Boost.Unordered容器,只需将文件/extra/boost_unordered.natvis作为“现有项”添加到Visual Studio项目中。
可视化结构
可视化效果与标准无序容器的类似。容器最多一次显示100个元素。每个集合元素的项目名称列为[i]
,其中i
是显示中的索引,从0
开始。每个映射元素的项目名称默认为[{key-display}]
。例如,如果第一个元素是键值对("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框架查看。
迭代器的显示方式与其标准对应物类似。迭代器显示方式与其指向的元素相同。结束迭代器简单地显示为{ end iterator }
。
花哨指针
如果您在分配器中使用花哨的指针,例如boost::interprocess::offset_ptr
,则容器可视化也适用。虽然这种情况很少见,但Boost.Unordered具有natvis自定义点来支持任何类型的花哨指针。boost::interprocess::offset_ptr
在Boost.Interprocess库中已经定义了支持,您可以按照文件/extra/boost_unordered.natvis结尾附近注释中包含的说明,为自己的类型添加支持。
GDB漂亮打印器
所有容器和迭代器都有自定义GDB漂亮打印器。
在你的项目中使用
使用漂亮打印器时,始终必须像下面一样启用漂亮打印。这通常是一次性设置。
(gdb) set print pretty on
默认情况下,如果您编译成ELF二进制格式,则您的二进制文件将包含Boost.Unordered漂亮打印器。要使用嵌入式漂亮打印器,请确保您允许自动加载,如下所示。每次加载GDB时都必须执行此操作,或者将其添加到“.gdbinit”文件中。
(gdb) add-auto-load-safe-path [/path/to/executable]
您可以选择通过定义BOOST_ALL_NO_EMBEDDED_GDB_SCRIPTS
来不嵌入漂亮打印器编译您的二进制文件,这将禁用所有具有此功能的Boost库的嵌入式GDB漂亮打印器。
您可以从非嵌入式Python脚本外部加载漂亮打印器。使用source
命令添加脚本/extra/boost_unordered_printers.py,如下所示。
(gdb) source [/path/to/boost]/libs/unordered/extra/boost_unordered_printers.py
可视化结构
可视化效果与标准无序容器类似。映射容器显示从键到映射值的关联。集合容器显示从索引到值的关联。迭代器要么与其项目一起显示,要么作为结束迭代器显示。以下是示例boost::unordered_map
、示例boost::unordered_set
及其各自的开始和结束迭代器可能显示的内容。
(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漂亮打印器查看。
对于启用了容器统计信息的开地址容器,您可以通过在GDB内部调用容器上的get_stats()
来获取这些统计信息。这在GDB中被重写为xmethod,因此它不会调用任何C++同步代码。请参阅以下打印输出以了解预期格式的示例。
(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
,则漂亮打印器也适用。虽然这种情况很少见,但Boost.Unordered具有GDB漂亮打印器自定义点来支持任何类型的花哨指针。boost::interprocess::offset_ptr
在Boost.Interprocess库中已经定义了支持,您可以按照文件/extra/boost_unordered_printers.py结尾附近注释中包含的说明,为自己的类型添加支持。
基准测试
boost::unordered_[multi]set
所有基准测试都是使用unordered_set<unsigned int>
(非重复)和unordered_multiset<unsigned int>
(重复)创建的。源代码可以在这里找到。
插入基准测试插入n
个随机值,其中n
介于10,000和300万之间。对于重复的基准测试,相同的随机值平均重复5次。
擦除基准测试随机擦除所有n
个元素,直到容器为空。按键擦除使用erase(const key_type&)
在每次操作中删除整个等效元素组。
成功的查找基准测试是通过按其原始插入顺序查找所有n
个值来完成的。
不成功的查找基准测试使用n
个随机生成的整数,但使用不同的种子值。
GCC 12 + libstdc++-v3,x64
擦除
非重复元素 |
重复元素 |
重复元素, |
---|---|---|
按键,重复元素 |
按键,重复元素, |
成功查找
非重复元素 |
重复元素 |
重复元素, |
---|
不成功查找
非重复元素 |
重复元素 |
重复元素, |
---|
Clang 15 + libc++,x64
擦除
非重复元素 |
重复元素 |
重复元素, |
---|---|---|
按键,重复元素 |
按键,重复元素, |
成功查找
非重复元素 |
重复元素 |
重复元素, |
---|
不成功查找
非重复元素 |
重复元素 |
重复元素, |
---|
Visual Studio 2022 + Dinkumware,x64
擦除
非重复元素 |
重复元素 |
重复元素, |
---|---|---|
按键,重复元素 |
按键,重复元素, |
成功查找
非重复元素 |
重复元素 |
重复元素, |
---|
不成功查找
非重复元素 |
重复元素 |
重复元素, |
---|
boost::unordered_(flat|node)_map
所有基准测试都是使用
-
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>
源代码可以在这里找到。
插入基准测试插入n
个随机值,其中n
介于10,000和1000万之间。
擦除基准测试会遍历n
个元素,并擦除键为奇数的元素(平均 50%)。
成功的查找基准测试是通过按其原始插入顺序查找所有n
个值来完成的。
不成功的查找基准测试使用n
个随机生成的整数,但使用不同的种子值。
GCC 12,x64
运行插入操作 |
运行擦除操作 |
查找成功 |
查找失败 |
---|
Clang 15,x64
运行插入操作 |
运行擦除操作 |
查找成功 |
查找失败 |
---|
Visual Studio 2022,x64
运行插入操作 |
运行擦除操作 |
查找成功 |
查找失败 |
---|
Clang 12,ARM64
运行插入操作 |
运行擦除操作 |
查找成功 |
查找失败 |
---|
GCC 12,x86
运行插入操作 |
运行擦除操作 |
查找成功 |
查找失败 |
---|
Clang 15,x86
运行插入操作 |
运行擦除操作 |
查找成功 |
查找失败 |
---|
Visual Studio 2022,x86
运行插入操作 |
运行擦除操作 |
查找成功 |
查找失败 |
---|
boost::concurrent_(flat|node)_map
所有基准测试都是使用
-
oneapi::tbb::concurrent_hash_map<int, int>
-
带有 64 个子映射的
gtl::parallel_flat_hash_map<int, int>
-
boost::concurrent_flat_map<int, int>
-
boost::concurrent_node_map<int, int>
源代码可以在这里找到。
基准测试会使用多个线程 *T*(1 到 16 之间),这些线程并发执行在**更新**、**查找成功**和**查找失败**之间随机选择的运算。运算中使用的键遵循齐普夫定律,并具有不同的 *倾斜* 参数:倾斜度越高,键在覆盖范围的较低值中越集中。
boost::concurrent_flat_map
和 boost::concurrent_node_map
使用常规访问和批量访问进行测试:在后者的情况下,查找键会缓存在本地数组中,然后每次缓冲区达到bulk_visit_size
时一起处理。
GCC 12,x64
500k 次更新,450万次查找 |
500k 次更新,450万次查找 |
500k 次更新,450万次查找 |
---|
500万次更新,4500万次查找 |
500万次更新,4500万次查找 |
500万次更新,4500万次查找 |
---|
Clang 15,x64
500k 次更新,450万次查找 |
500k 次更新,450万次查找 |
500k 次更新,450万次查找 |
---|
500万次更新,4500万次查找 |
500万次更新,4500万次查找 |
500万次更新,4500万次查找 |
---|
Visual Studio 2022,x64
500k 次更新,450万次查找 |
500k 次更新,450万次查找 |
500k 次更新,450万次查找 |
---|
500万次更新,4500万次查找 |
500万次更新,4500万次查找 |
500万次更新,4500万次查找 |
---|
Clang 12,ARM64
500k 次更新,450万次查找 |
500k 次更新,450万次查找 |
500k 次更新,450万次查找 |
---|
500万次更新,4500万次查找 |
500万次更新,4500万次查找 |
500万次更新,4500万次查找 |
---|
GCC 12,x86
500k 次更新,450万次查找 |
500k 次更新,450万次查找 |
500k 次更新,450万次查找 |
---|
500万次更新,4500万次查找 |
500万次更新,4500万次查找 |
500万次更新,4500万次查找 |
---|
Clang 15,x86
500k 次更新,450万次查找 |
500k 次更新,450万次查找 |
500k 次更新,450万次查找 |
---|
500万次更新,4500万次查找 |
500万次更新,4500万次查找 |
500万次更新,4500万次查找 |
---|
Visual Studio 2022,x86
500k 次更新,450万次查找 |
500k 次更新,450万次查找 |
500k 次更新,450万次查找 |
---|
500万次更新,4500万次查找 |
500万次更新,4500万次查找 |
500万次更新,4500万次查找 |
---|
实现原理
闭址容器
boost::unordered_[multi]set
和 boost::unordered_[multi]map
遵守无序关联容器的标准要求,因此接口是固定的。但是仍然有一些实现决策需要做出。优先级是符合标准和可移植性。
关于哈希表的维基百科文章对一般哈希表的实现问题进行了很好的总结。
数据结构
通过指定访问容器桶的接口,标准几乎要求哈希表使用闭地址法。
可以想象编写一个使用其他方法的哈希表。例如,它可以使用开地址法,并使用查找链充当桶,但这样做会有一些严重的问题。
-
标准要求元素的指针不会失效,因此元素不能存储在一个数组中,而需要一个间接层——从而损失了效率和大部分内存增益,这是开地址法的主要优点。
-
局部迭代器效率会非常低,并且可能无法满足复杂性要求。
-
迭代器失效时间的限制也是如此。由于开地址法在发生大量冲突时性能会严重下降,因此这些限制可能会阻止在真正需要时进行重新哈希。最大负载因子可以设置为较低的值来解决此问题——但是标准要求将其初始设置为 1.0。
-
而且,由于标准是针对闭地址法编写的,如果性能没有反映这一点,用户会感到惊讶。
因此,使用闭地址法。
桶的数量
有两种常用的方法来选择哈希表中桶的数量。一种是使用素数个桶,另一种是使用 2 的幂。
使用素数个桶,并通过使用哈希函数结果的模来选择桶通常会得到一个好的结果。缺点是所需的模运算相当昂贵。这在大多数情况下是容器过去所做的。
使用 2 的幂允许更快地选择要使用的桶,但代价是丢失哈希值的高位。对于某些专门设计的哈希函数,可以这样做并仍然获得良好的结果,但由于容器可以采用任意哈希函数,因此无法依赖此方法。
为了避免这种情况,可以对哈希函数应用转换,例如参见Thomas Wang 关于整数哈希函数的文章。不幸的是,像 Wang 的转换那样需要知道哈希值中的位数,因此仅在 size_t
为 64 位时才使用。
从 1.79.0 版本开始,改为使用斐波那契哈希。在此实现中,桶号通过使用 (h * m) >> (w - k)
确定,其中 h
是哈希值,m
是 2^w
除以黄金比例,w
是字大小(32 或 64),2^k
是桶的数量。这在速度和分布之间提供了良好的折衷。
从 1.80.0 版本开始,素数与复杂的模运算一起用于选择桶的数量。这消除了对用户哈希函数结果的“混合”的需求,而 1.79.0 版本使用了这种方法。
开址容器
C++ 标准对无序关联容器的规范对允许的实现施加了严格的限制,其中最重要的是隐式假设了闭地址法。稍微放宽此规范可以打开提供容器变体的可能性,这些变体可以充分利用开地址法技术。
boost::unordered_flat_set
/unordered_node_set
和 boost::unordered_flat_map
/unordered_node_map
的设计遵循 Peter Dimov 的Boost.Unordered 开发计划。我们在此讨论最相关的原则。
哈希函数
鉴于其丰富的功能和跨平台互操作性,boost::hash
仍然是开地址容器的默认哈希函数。碰巧的是,对于整数和其他基本类型,boost::hash
不具备开地址法所需的统计特性;为了应对这个问题,我们实现了一个后混合阶段
a ← h mulx C,
h ← high(a) xor low(a),
其中 mulx 是扩展乘法(在 64 位架构中为 128 位,在 32 位环境中为 64 位),high 和 low 分别是扩展字的上半部分和下半部分。在 64 位架构中,C 是 264∕φ 的整数部分,而在 32 位中,C = 0xE817FB2Du 是从Steele 和 Vigna (2021)获得的。
当使用直接适合开地址法的哈希函数时,可以通过专用的hash_is_avalanching
特性选择退出后混合。字符串类型的 boost::hash
特化被标记为 avalanching(雪崩)。
平台互操作性
只要它们的 std::size_t
大小相同,并且用户提供的哈希函数和相等谓词也具有互操作性,boost::unordered_flat_set
/unordered_node_set
和 boost::unordered_flat_map
/unordered_node_map
的可观察行为在不同的编译器上确定性地相同——这包括对于相同的操作序列,元素以完全相同的方式排序。
并发容器
Boost.Unordered 开地址容器使用的相同数据结构也被选为boost::concurrent_flat_set
/boost::concurrent_node_set
和 boost::concurrent_flat_map
/boost::concurrent_node_map
的基础。
-
开地址法比闭地址法替代方案更快,无论是在非并发场景还是并发场景中。
-
开地址布局非常适合并发访问和修改,并且锁的开销最小。特别是,元数据数组可用于实现查找,直到实际元素比较的最后一步都是无锁的。
-
与 Boost.Unordered flat 容器的布局兼容性允许在并发容器及其非并发对应物之间快速传输所有元素,反之亦然。
参考
类模板 unordered_map
boost::unordered_map
— 一个无序关联容器,它将唯一键与另一个值关联起来。
概要
// #include <boost/unordered/unordered_map.hpp> namespace boost { template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T>>> class unordered_map { public: // types using key_type = Key; using mapped_type = T; using value_type = std::pair<const Key, T>; using hasher = Hash; using key_equal = Pred; using allocator_type = Allocator; using pointer = typename std::allocator_traits<Allocator>::pointer; using const_pointer = typename std::allocator_traits<Allocator>::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 unordered_map(); explicit unordered_map(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); template<class InputIterator> 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()); unordered_map(const unordered_map& other); unordered_map(unordered_map&& other); template<class InputIterator> unordered_map(InputIterator f, InputIterator l, const allocator_type& a); explicit unordered_map(const Allocator& a); unordered_map(const unordered_map& other, const Allocator& a); unordered_map(unordered_map&& other, const Allocator& a); unordered_map(std::initializer_list<value_type> il, size_type n = implementation-defined const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); unordered_map(size_type n, const allocator_type& a); unordered_map(size_type n, const hasher& hf, const allocator_type& a); template<class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a); template<class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a); unordered_map(std::initializer_list<value_type> il, const allocator_type& a); unordered_map(std::initializer_list<value_type> il, size_type n, const allocator_type& a); unordered_map(std::initializer_list<value_type> il, size_type n, const hasher& hf, const allocator_type& a); ~unordered_map(); unordered_map& operator=(const unordered_map& other); unordered_map& operator=(unordered_map&& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value && boost::is_nothrow_move_assignable_v<Hash> && boost::is_nothrow_move_assignable_v<Pred>); unordered_map& operator=(std::initializer_list<value_type>); allocator_type get_allocator() const noexcept; // iterators iterator begin() noexcept; const_iterator begin() const noexcept; iterator end() noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; // capacity [[nodiscard]] bool empty() const noexcept; size_type size() const noexcept; size_type max_size() const noexcept; // modifiers template<class... Args> std::pair<iterator, bool> emplace(Args&&... args); template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args); std::pair<iterator, bool> insert(const value_type& obj); std::pair<iterator, bool> insert(value_type&& obj); template<class P> std::pair<iterator, bool> insert(P&& obj); iterator insert(const_iterator hint, const value_type& obj); iterator insert(const_iterator hint, value_type&& obj); template<class P> iterator insert(const_iterator hint, P&& obj); template<class InputIterator> void insert(InputIterator first, InputIterator last); void insert(std::initializer_list<value_type>); template<class... Args> std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args); template<class... Args> std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args); template<class K, class... Args> std::pair<iterator, bool> try_emplace(K&& k, Args&&... args); template<class... Args> iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); template<class... Args> iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); template<class K, class... Args> iterator try_emplace(const_iterator hint, K&& k, Args&&... args); template<class M> std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj); template<class M> std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj); template<class K, class M> std::pair<iterator, bool> insert_or_assign(K&& k, M&& obj); template<class M> iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); template<class M> iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); template<class K, class M> iterator insert_or_assign(const_iterator hint, K&& k, M&& obj); node_type extract(const_iterator position); node_type extract(const key_type& k); template<class K> node_type extract(K&& k); insert_return_type insert(node_type&& nh); iterator insert(const_iterator hint, node_type&& nh); iterator erase(iterator position); iterator erase(const_iterator position); size_type erase(const key_type& k); template<class K> size_type erase(K&& k); iterator erase(const_iterator first, const_iterator last); void quick_erase(const_iterator position); void erase_return_void(const_iterator position); void swap(unordered_map& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value && boost::is_nothrow_swappable_v<Hash> && boost::is_nothrow_swappable_v<Pred>); void clear() noexcept; template<class H2, class P2> void merge(unordered_map<Key, T, H2, P2, Allocator>& source); template<class H2, class P2> void merge(unordered_map<Key, T, H2, P2, Allocator>&& source); template<class H2, class P2> void merge(unordered_multimap<Key, T, H2, P2, Allocator>& source); template<class H2, class P2> void merge(unordered_multimap<Key, T, H2, P2, Allocator>&& source); // observers hasher hash_function() const; key_equal key_eq() const; // map operations iterator find(const key_type& k); const_iterator find(const key_type& k) const; template<class K> iterator find(const K& k); template<class K> const_iterator find(const K& k) const; template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq); template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> const_iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const; size_type count(const key_type& k) const; template<class K> size_type count(const K& k) const; bool contains(const key_type& k) const; template<class K> bool contains(const K& k) const; std::pair<iterator, iterator> equal_range(const key_type& k); std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const; template<class K> std::pair<iterator, iterator> equal_range(const K& k); template<class K> std::pair<const_iterator, const_iterator> equal_range(const K& k) const; // element access mapped_type& operator[](const key_type& k); mapped_type& operator[](key_type&& k); template<class K> mapped_type& operator[](K&& k); mapped_type& at(const key_type& k); const mapped_type& at(const key_type& k) const; template<class K> mapped_type& at(const K& k); template<class K> const mapped_type& at(const K& k) const; // bucket interface size_type bucket_count() const noexcept; size_type max_bucket_count() const noexcept; size_type bucket_size(size_type n) const; size_type bucket(const key_type& k) const; template<class K> size_type bucket(const K& k) const; local_iterator begin(size_type n); const_local_iterator begin(size_type n) const; local_iterator end(size_type n); const_local_iterator end(size_type n) const; const_local_iterator cbegin(size_type n) const; const_local_iterator cend(size_type n) const; // hash policy float load_factor() const noexcept; float max_load_factor() const noexcept; void max_load_factor(float z); void rehash(size_type n); void reserve(size_type n); }; // Deduction Guides template<class InputIterator, class Hash = boost::hash<iter-key-type<InputIterator>>, class Pred = std::equal_to<iter-key-type<InputIterator>>, class Allocator = std::allocator<iter-to-alloc-type<InputIterator>>> unordered_map(InputIterator, InputIterator, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, Hash, Pred, Allocator>; template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T>>> unordered_map(std::initializer_list<std::pair<Key, T>>, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_map<Key, T, Hash, Pred, Allocator>; template<class InputIterator, class Allocator> unordered_map(InputIterator, InputIterator, typename see below::size_type, Allocator) -> unordered_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, boost::hash<iter-key-type<InputIterator>>, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class InputIterator, class Allocator> unordered_map(InputIterator, InputIterator, Allocator) -> unordered_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, boost::hash<iter-key-type<InputIterator>>, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class InputIterator, class Hash, class Allocator> unordered_map(InputIterator, InputIterator, typename see below::size_type, Hash, Allocator) -> unordered_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, Hash, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class Key, class T, class Allocator> unordered_map(std::initializer_list<std::pair<Key, T>>, typename see below::size_type, Allocator) -> unordered_map<Key, T, boost::hash<Key>, std::equal_to<Key>, Allocator>; template<class Key, class T, class Allocator> unordered_map(std::initializer_list<std::pair<Key, T>>, Allocator) -> unordered_map<Key, T, boost::hash<Key>, std::equal_to<Key>, Allocator>; template<class Key, class T, class Hash, class Allocator> unordered_map(std::initializer_list<std::pair<Key, T>>, typename see below::size_type, Hash, Allocator) -> unordered_map<Key, T, Hash, std::equal_to<Key>, Allocator>; // Equality Comparisons template<class Key, class T, class Hash, class Pred, class Alloc> bool operator==(const unordered_map<Key, T, Hash, Pred, Alloc>& x, const unordered_map<Key, T, Hash, Pred, Alloc>& y); template<class Key, class T, class Hash, class Pred, class Alloc> bool operator!=(const unordered_map<Key, T, Hash, Pred, Alloc>& x, const unordered_map<Key, T, Hash, Pred, Alloc>& y); // swap template<class Key, class T, class Hash, class Pred, class Alloc> void swap(unordered_map<Key, T, Hash, Pred, Alloc>& x, unordered_map<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y))); // Erasure template<class K, class T, class H, class P, class A, class Predicate> typename unordered_map<K, T, H, P, A>::size_type erase_if(unordered_map<K, T, H, P, A>& c, Predicate pred); // Pmr aliases (C++17 and up) namespace unordered::pmr { template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>> using unordered_map = boost::unordered_map<Key, T, Hash, Pred, std::pmr::polymorphic_allocator<std::pair<const Key, T>>>; } }
描述
模板参数
键 |
|
T |
|
哈希 |
一个一元函数对象类型,充当 |
Pred |
一个二元函数对象,它在 |
分配器 |
一个分配器,其值类型与容器的值类型相同。支持使用花哨指针的分配器。 |
元素被组织成桶。具有相同哈希码的键存储在同一个桶中。
桶的数量可以通过调用插入操作自动增加,或者作为调用重新哈希的结果。
配置宏
BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0
全局定义此宏以支持加载保存到 Boost.Serialization 存档中的 unordered_map
,该存档使用 Boost 1.84 之前的版本。
类型定义
typedef implementation-defined iterator;
一个迭代器,其值类型为 value_type
。
迭代器类别至少是前向迭代器。
可转换为 const_iterator
。
typedef implementation-defined const_iterator;
一个常量迭代器,其值类型为 value_type
。
迭代器类别至少是前向迭代器。
typedef implementation-defined local_iterator;
一个与迭代器具有相同值类型、差值类型和指针和引用类型的迭代器。
local_iterator
对象可用于遍历单个桶。
typedef implementation-defined const_local_iterator;
一个与 const_iterator
具有相同值类型、差值类型和指针和引用类型的常量迭代器。
const_local_iterator
对象可用于遍历单个桶。
typedef implementation-defined node_type;
一个用于保存已提取容器元素的类,模拟NodeHandle。
typedef implementation-defined insert_return_type;
内部类模板的特化
template<class Iterator, class NodeType>
struct insert_return_type // name is exposition only
{
Iterator position;
bool inserted;
NodeType node;
};
其中 Iterator
= iterator
和 NodeType
= node_type
。
构造函数
默认构造函数
unordered_map();
使用 hasher()
作为哈希函数、key_equal()
作为键相等谓词、allocator_type()
作为分配器和 1.0 的最大负载因子来构造一个空容器。
后置条件 |
|
要求 |
如果使用默认值,则 |
桶计数构造函数
explicit unordered_map(size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。
后置条件 |
|
要求 |
如果使用默认值,则 |
迭代器范围构造函数
template<class InputIterator>
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());
使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `[f, l)` 区间内的元素插入其中。
要求 |
如果使用默认值,则 |
复制构造函数
unordered_map(unordered_map const& other);
复制构造函数。复制包含的元素、哈希函数、谓词、最大负载因子和分配器。
如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。
要求 |
`value_type` 可复制构造。 |
带分配器的迭代器范围构造函数
template<class InputIterator>
unordered_map(InputIterator f, InputIterator l, const allocator_type& a);
使用 `a` 作为分配器,使用默认的哈希函数和键相等谓词,最大负载因子为 `1.0`,构造一个空容器,并将 `[f, l)` 区间内的元素插入其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带分配器的复制构造函数
unordered_map(unordered_map const& other, Allocator const& a);
构造一个容器,复制 `other` 包含的元素、哈希函数、谓词、最大负载因子,但使用分配器 `a`。
带分配器的移动构造函数
unordered_map(unordered_map&& other, Allocator const& a);
构造一个容器,移动 `other` 包含的元素,并具有哈希函数、谓词和最大负载因子,但使用分配器 `a`。
注释 |
这是使用 Boost.Move 实现的。 |
要求 |
`value_type` 可移动插入。 |
初始化列表构造函数
unordered_map(std::initializer_list<value_type> il,
size_type n = implementation-defined
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `il` 中的元素插入其中。
要求 |
如果使用默认值,则 |
带分配器的桶计数构造函数
unordered_map(size_type n, allocator_type const& a);
使用 `hf` 作为哈希函数,使用默认的哈希函数和键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。
后置条件 |
|
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带哈希函数和分配器的桶计数构造函数
unordered_map(size_type n, hasher const& hf, allocator_type const& a);
使用 `hf` 作为哈希函数,使用默认的键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。
后置条件 |
|
要求 |
`key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的迭代器范围构造函数
template<class InputIterator>
unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a);
使用 `a` 作为分配器,使用默认的哈希函数和键相等谓词,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `[f, l)` 区间内的元素插入其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和哈希函数的迭代器范围构造函数
template<class InputIterator>
unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a);
使用 `hf` 作为哈希函数,`a` 作为分配器,使用默认的键相等谓词,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `[f, l)` 区间内的元素插入其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
带分配器的初始化列表构造函数
unordered_map(std::initializer_list<value_type> il, const allocator_type& a);
使用 `a` 作为分配器,最大负载因子为 1.0,构造一个空容器,并将 `il` 中的元素插入其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的初始化列表构造函数
unordered_map(std::initializer_list<value_type> il, size_type n, const allocator_type& a);
使用 `a` 作为分配器,最大负载因子为 1.0,构造一个至少包含 `n` 个桶的空容器,并将 `il` 中的元素插入其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数、哈希函数和分配器的初始化列表构造函数
unordered_map(std::initializer_list<value_type> il, size_type n, const hasher& hf,
const allocator_type& a);
使用 `hf` 作为哈希函数,`a` 作为分配器,最大负载因子为 1.0,构造一个至少包含 `n` 个桶的空容器,并将 `il` 中的元素插入其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
赋值
复制赋值
unordered_map& operator=(unordered_map const& other);
赋值运算符。复制包含的元素、哈希函数、谓词和最大负载因子,但不复制分配器。
如果存在 `Alloc::propagate_on_container_copy_assignment` 并且 `Alloc::propagate_on_container_copy_assignment::value` 为 `true`,则分配器将被覆盖;否则,将使用现有分配器创建复制的元素。
要求 |
`value_type` 可复制构造。 |
移动赋值
unordered_map& operator=(unordered_map&& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
boost::is_nothrow_move_assignable_v<Hash> &&
boost::is_nothrow_move_assignable_v<Pred>);
移动赋值运算符。
如果存在 `Alloc::propagate_on_container_move_assignment` 并且 `Alloc::propagate_on_container_move_assignment::value` 为 `true`,则分配器将被覆盖;否则,将使用现有分配器创建移动的元素。
要求 |
`value_type` 可移动构造。 |
迭代器
begin
iterator begin() noexcept;
const_iterator begin() const noexcept;
返回值 |
指向容器第一个元素的迭代器,如果容器为空,则返回容器的尾后值。 |
修改器
emplace
template<class... Args> std::pair<iterator, bool> emplace(Args&&... args);
当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。
要求 |
`value_type` 可从 `args` 就地构造 到 `X` 中。 |
返回值 |
返回类型的布尔分量如果进行了插入则为 true。 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 如果 `args…` 的形式为 `k,v`,它会延迟构造整个对象,直到确定应该插入元素为止,只使用 `k` 参数进行检查。当 map 的 `key_type` 可移动构造或 `k` 参数是 `key_type` 时,会发生这种优化。 |
emplace_hint
template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。
`position` 是关于应插入元素位置的建议。
要求 |
`value_type` 可从 `args` 就地构造 到 `X` 中。 |
返回值 |
如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 如果 `args…` 的形式为 `k,v`,它会延迟构造整个对象,直到确定应该插入元素为止,只使用 `k` 参数进行检查。当 map 的 `key_type` 可移动构造或 `k` 参数是 `key_type` 时,会发生这种优化。 |
复制插入
std::pair<iterator, bool> insert(const value_type& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
要求 |
`value_type` 可 复制插入。 |
返回值 |
返回类型的布尔分量如果进行了插入则为 true。 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
移动插入
std::pair<iterator, bool> insert(value_type&& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
要求 |
`value_type` 可 移动插入。 |
返回值 |
返回类型的布尔分量如果进行了插入则为 true。 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
就地插入
template<class P> std::pair<iterator, bool> insert(P&& obj);
通过执行 `emplace(std::forward<P>(value))` 将元素插入容器中。
只有当 `std::is_constructible<value_type, P&&>::value` 为 `true` 时,才参与重载解析。
返回值 |
返回类型的布尔分量如果进行了插入则为 true。 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
带提示的复制插入
iterator insert(const_iterator hint, const value_type& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
`hint` 是关于应插入元素位置的建议。
要求 |
`value_type` 可 复制插入。 |
返回值 |
如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
带提示的移动插入
iterator insert(const_iterator hint, value_type&& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
`hint` 是关于应插入元素位置的建议。
要求 |
`value_type` 可 移动插入。 |
返回值 |
如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
带提示的就地插入
template<class P> iterator insert(const_iterator hint, P&& obj);
通过执行 `emplace_hint(hint, std::forward<P>(value))` 将元素插入容器中。
只有当 `std::is_constructible<value_type, P&&>::value` 为 `true` 时,才参与重载解析。
`hint` 是关于应插入元素位置的建议。
返回值 |
如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
插入迭代器范围
template<class InputIterator> void insert(InputIterator first, InputIterator last);
将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。
要求 |
`value_type` 可从 `*first` 就地构造 到 `X` 中。 |
抛出 |
插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
插入初始化列表
void insert(std::initializer_list<value_type>);
将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。
要求 |
`value_type` 可 复制插入 到容器中。 |
抛出 |
插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
try_emplace
template<class... Args>
std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args);
template<class... Args>
std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args);
template<class K, class... Args>
std::pair<iterator, bool> try_emplace(K&& k, Args&&... args)
如果容器中不存在具有键 `k` 的元素,则将新元素插入容器中。
如果存在具有键 `k` 的元素,则此函数不执行任何操作。
返回值 |
返回类型的布尔分量如果进行了插入则为 true。 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
此函数类似于 emplace,但 `value_type` 的构造使用
而不是 emplace,它只是将所有参数转发到 `value_type` 的构造函数。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 `template<class K, class... Args>` 重载只有在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义,并且 `iterator` 和 `const_iterator` 都不可以隐式转换为 `K` 时才参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这使得异构查找能够避免实例化 `Key` 类型实例的成本。 |
带提示的 try_emplace
template<class... Args>
iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args);
template<class... Args>
iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args);
template<class K, class... Args>
iterator try_emplace(const_iterator hint, K&& k, Args&&... args);
如果容器中不存在具有键 `k` 的元素,则将新元素插入容器中。
如果存在具有键 `k` 的元素,则此函数不执行任何操作。
`hint` 是关于应插入元素位置的建议。
返回值 |
如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
此函数类似于 emplace_hint,但 `value_type` 的构造使用
而不是 emplace_hint,它只是将所有参数转发到 `value_type` 的构造函数。 标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 `template<class K, class... Args>` 重载只有在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义,并且 `iterator` 和 `const_iterator` 都不可以隐式转换为 `K` 时才参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这使得异构查找能够避免实例化 `Key` 类型实例的成本。 |
insert_or_assign
template<class M>
std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj);
template<class M>
std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj);
template<class K, class M>
std::pair<iterator, bool> insert_or_assign(K&& k, M&& obj);
将新元素插入容器中,或通过赋值给包含的值来更新现有元素。
如果存在具有键 `k` 的元素,则通过赋值 `std::forward<M>(obj)` 来更新它。
如果没有这样的元素,则将其添加到容器中,如下所示:
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
返回值 |
返回类型的布尔分量如果进行了插入则为 true。 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 `template<class K, class M>` 只有在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义时才参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这使得异构查找能够避免实例化 `Key` 类型实例的成本。 |
带提示的 insert_or_assign
template<class M>
iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj);
template<class M>
iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj);
template<class K, class M>
iterator insert_or_assign(const_iterator hint, K&& k, M&& obj);
将新元素插入容器中,或通过赋值给包含的值来更新现有元素。
如果存在具有键 `k` 的元素,则通过赋值 `std::forward<M>(obj)` 来更新它。
如果没有这样的元素,则将其添加到容器中,如下所示:
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
`hint` 是关于应插入元素位置的建议。
返回值 |
如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 `template<class K, class M>` 只有在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义时才参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这使得异构查找能够避免实例化 `Key` 类型实例的成本。 |
通过迭代器提取
node_type extract(const_iterator position);
删除 `position` 指向的元素。
返回值 |
拥有该元素的 `node_type`。 |
注释 |
使用此方法提取的节点可以插入到兼容的 `unordered_multimap` 中。 |
通过键提取
node_type extract(const key_type& k);
template<class K> node_type extract(K&& k);
移除键值等效于k
的元素。
返回值 |
如果找到,则返回拥有该元素的 |
抛出 |
只有当 |
注释 |
使用此方法提取的节点可以插入到兼容的 `unordered_multimap` 中。 只有当 |
使用node_handle
插入
insert_return_type insert(node_type&& nh);
如果nh
为空,则没有效果。
否则,当且仅当容器中没有具有等效键的元素时,插入nh
拥有的元素。
要求 |
|
返回值 |
如果 否则,如果已经存在具有等效键的元素,则返回一个 否则,如果插入成功,则返回一个 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 这可以用来插入从兼容的 |
使用提示和node_handle
插入
iterator insert(const_iterator hint, node_type&& nh);
如果nh
为空,则没有效果。
否则,当且仅当容器中没有具有等效键的元素时,插入nh
拥有的元素。
如果容器中已经存在具有等效键的元素,则对nh
没有影响(即nh
仍然包含该节点)。
`hint` 是关于应插入元素位置的建议。
要求 |
|
返回值 |
如果 如果容器中已经存在具有等效键的元素,则返回指向该元素的迭代器。 否则,返回指向新插入元素的迭代器。 |
抛出 |
如果由除对hasher的调用以外的操作抛出异常,则该函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 这可以用来插入从兼容的 |
按位置删除
iterator erase(iterator position);
iterator erase(const_iterator position);
删除position
指向的元素。
返回值 |
删除之前 |
抛出 |
只有当 |
注释 |
在旧版本中,这可能效率低下,因为它必须搜索多个桶才能找到返回迭代器的位置。数据结构已更改,因此这种情况不再发生,并且替代删除方法已被弃用。 |
按键删除
size_type erase(const key_type& k);
template<class K> size_type erase(K&& k);
删除所有键值等效于k
的元素。
返回值 |
删除的元素数量。 |
抛出 |
只有当 |
注释 |
只有当 |
删除范围
iterator erase(const_iterator first, const_iterator last);
删除从first
到last
范围内的元素。
返回值 |
删除元素之后的迭代器 - 即 |
抛出 |
只有当 在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。 |
快速删除(quick_erase)
void quick_erase(const_iterator position);
删除position
指向的元素。
抛出 |
只有当 在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。 |
注释 |
实现此方法是因为从erase返回指向下一个元素的迭代器代价很高,但是容器已经重新设计,这种情况不再发生。因此,此方法现已弃用。 |
返回void的删除 (erase_return_void)
void erase_return_void(const_iterator position);
删除position
指向的元素。
抛出 |
只有当 在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。 |
注释 |
实现此方法是因为从erase返回指向下一个元素的迭代器代价很高,但是容器已经重新设计,这种情况不再发生。因此,此方法现已弃用。 |
交换 (swap)
void swap(unordered_map& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
boost::is_nothrow_swappable_v<Hash> &&
boost::is_nothrow_swappable_v<Pred>);
将容器的内容与参数交换。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。
抛出 |
除非 |
注释 |
异常规范与C++11标准略有不同,因为相等谓词和哈希函数使用它们的复制构造函数进行交换。 |
合并 (merge)
template<class H2, class P2>
void merge(unordered_map<Key, T, H2, P2, Allocator>& source);
template<class H2, class P2>
void merge(unordered_map<Key, T, H2, P2, Allocator>&& source);
template<class H2, class P2>
void merge(unordered_multimap<Key, T, H2, P2, Allocator>& source);
template<class H2, class P2>
void merge(unordered_multimap<Key, T, H2, P2, Allocator>&& source);
尝试通过迭代source
并将source
中不包含在*this
中的任何节点提取出来,然后将其插入到*this
中,来“合并”两个容器。
因为source
可以具有不同的哈希函数和键相等谓词,所以source
中每个节点的键将使用this->hash_function()
重新哈希,然后根据需要使用this->key_eq()
进行比较。
如果this->get_allocator() != source.get_allocator()
,则此函数的行为未定义。
此函数不复制或移动任何元素,而是简单地将节点从source
重新定位到*this
。
注释 |
|
查找
查找 (find)
iterator find(const key_type& k);
const_iterator find(const key_type& k) const;
template<class K>
iterator find(const K& k);
template<class K>
const_iterator find(const K& k) const;
template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate>
iterator find(CompatibleKey const& k, CompatibleHash const& hash,
CompatiblePredicate const& eq);
template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate>
const_iterator find(CompatibleKey const& k, CompatibleHash const& hash,
CompatiblePredicate const& eq) const;
返回值 |
指向键值等效于 |
注释 |
包含 只有当 |
计数 (count)
size_type count(const key_type& k) const;
template<class K>
size_type count(const K& k) const;
返回值 |
键值等效于 |
注释 |
只有当 |
包含 (contains)
bool contains(const key_type& k) const;
template<class K>
bool contains(const K& k) const;
返回值 |
一个布尔值,指示容器中是否存在键等于 |
注释 |
只有当 |
相等范围 (equal_range)
std::pair<iterator, iterator> equal_range(const key_type& k);
std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
template<class K>
std::pair<iterator, iterator> equal_range(const K& k);
template<class K>
std::pair<const_iterator, const_iterator> equal_range(const K& k) const;
返回值 |
包含所有键值等效于 |
注释 |
只有当 |
下标运算符 (operator[])
mapped_type& operator[](const key_type& k);
mapped_type& operator[](key_type&& k);
template<class K> mapped_type& operator[](K&& k);
效果 |
如果容器还不包含键值等效于 |
返回值 |
|
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 只有当 |
at方法
mapped_type& at(const key_type& k);
const mapped_type& at(const key_type& k) const;
template<class K> mapped_type& at(const K& k);
template<class K> const mapped_type& at(const K& k) const;
返回值 |
|
抛出 |
如果不存在这样的元素,则抛出类型为 |
注释 |
只有当 |
桶接口
桶 (bucket)
size_type bucket(const key_type& k) const;
template<class K> size_type bucket(const K& k) const;
返回值 |
将包含键为 |
后置条件 |
返回值小于 |
注释 |
只有当 |
begin
local_iterator begin(size_type n);
const_local_iterator begin(size_type n) const;
要求 |
|
返回值 |
指向索引为 |
end
local_iterator end(size_type n);
const_local_iterator end(size_type n) const;
要求 |
|
返回值 |
指向索引为 |
cbegin
const_local_iterator cbegin(size_type n) const;
要求 |
|
返回值 |
指向索引为 |
cend
const_local_iterator cend(size_type n) const;
要求 |
|
返回值 |
指向索引为 |
哈希策略
重新哈希 (rehash)
void rehash(size_type n);
更改桶的数量,使桶数至少为n
,并且负载因子小于或等于最大负载因子。在适用情况下,这将使与容器关联的bucket_count()
增加或减少。
当size() == 0
时,rehash(0)
将释放底层桶数组。
使迭代器失效,并更改元素的顺序。指向元素的指针和引用不会失效。
抛出 |
除非容器的哈希函数或比较函数抛出异常,否则该函数无效。 |
预留空间 (reserve)
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
类似,此函数可用于增加或减少容器中桶的数量。
使迭代器失效,并更改元素的顺序。指向元素的指针和引用不会失效。
抛出 |
除非容器的哈希函数或比较函数抛出异常,否则该函数无效。 |
推导指南
如果出现以下任何情况,则推导指南不会参与重载解析:
-
它具有
InputIterator
模板参数,并且为该参数推导的类型不符合输入迭代器的资格。 -
它具有
Allocator
模板参数,并且为该参数推导的类型不符合分配器的资格。 -
它具有
Hash
模板参数,并且为该参数推导的类型为整数类型或符合分配器资格的类型。 -
它具有
Pred
模板参数,并且为该参数推导的类型符合分配器资格的类型。
推导指南中的size_type
参数类型引用由推导指南推导出的容器类型的size_type
成员类型。其默认值与所选构造函数的默认值一致。
迭代器值类型 (iter-value-type)
template<class InputIterator> using iter-value-type = typename std::iterator_traits<InputIterator>::value_type; // exposition only
迭代器键类型 (iter-key-type)
template<class InputIterator> using iter-key-type = std::remove_const_t< std::tuple_element_t<0, iter-value-type<InputIterator>>>; // exposition only
迭代器映射类型 (iter-mapped-type)
template<class InputIterator> using iter-mapped-type = std::tuple_element_t<1, iter-value-type<InputIterator>>; // exposition only
迭代器到分配器类型 (iter-to-alloc-type)
template<class InputIterator> using iter-to-alloc-type = std::pair< std::add_const_t<std::tuple_element_t<0, iter-value-type<InputIterator>>>, std::tuple_element_t<1, iter-value-type<InputIterator>>>; // exposition only
相等比较
相等运算符 (operator==)
template<class Key, class T, class Hash, class Pred, class Alloc>
bool operator==(const unordered_map<Key, T, Hash, Pred, Alloc>& x,
const unordered_map<Key, T, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个具有相同键和相同值(使用operator==
比较值类型)的元素,则返回true
。
注释 |
如果两个容器没有等效的相等谓词,则行为未定义。 |
不相等运算符 (operator!=)
template<class Key, class T, class Hash, class Pred, class Alloc>
bool operator!=(const unordered_map<Key, T, Hash, Pred, Alloc>& x,
const unordered_map<Key, T, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个具有相同键和相同值(使用operator==
比较值类型)的元素,则返回false
。
注释 |
如果两个容器没有等效的相等谓词,则行为未定义。 |
交换
template<class Key, class T, class Hash, class Pred, class Alloc>
void swap(unordered_map<Key, T, Hash, Pred, Alloc>& x,
unordered_map<Key, T, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
交换x
和y
的内容。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。
效果 |
|
抛出 |
除非 |
注释 |
异常规范与C++11标准略有不同,因为相等谓词和哈希函数使用它们的复制构造函数进行交换。 |
erase_if
template<class K, class T, class H, class P, class A, class Predicate>
typename unordered_map<K, T, H, P, A>::size_type
erase_if(unordered_map<K, T, H, P, A>& c, Predicate pred);
遍历容器c
,并移除所有满足提供的谓词返回true
的元素。
返回值 |
被移除元素的数量。 |
注释 |
等价于
|
序列化
可以使用此库提供的API通过Boost.Serialization存档/检索unordered_map
。支持常规存档和XML存档。
将unordered_map保存到存档
将unordered_map
x
的所有元素保存到存档(XML存档)ar
中。
要求 |
|
从存档加载unordered_map
删除unordered_map
x
中所有预先存在的元素,并从存档(XML存档)ar
中插入原始unordered_map
other
保存到ar
读取的存储中的元素的已恢复副本。
要求 |
|
注意 |
如果存档是使用Boost 1.84之前的Boost版本保存的,则必须全局定义配置宏 |
将迭代器/const_iterator保存到存档
将iterator
(const_iterator
)it
的位置信息保存到存档(XML存档)ar
中。it
可以是end()
迭代器。
要求 |
|
从存档加载迭代器/const_iterator
使iterator
(const_iterator
)it
指向原始iterator
(const_iterator
)在存档(XML存档)ar
读取的存储中保存的已恢复位置。
要求 |
如果 |
类模板 unordered_multimap
boost::unordered_multimap
— 一个无序关联容器,它将键与另一个值关联。同一个键可以存储多次。
概要
// #include <boost/unordered/unordered_map.hpp> namespace boost { template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T>>> class unordered_multimap { public: // types using key_type = Key; using mapped_type = T; using value_type = std::pair<const Key, T>; using hasher = Hash; using key_equal = Pred; using allocator_type = Allocator; using pointer = typename std::allocator_traits<Allocator>::pointer; using const_pointer = typename std::allocator_traits<Allocator>::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 unordered_multimap(); explicit unordered_multimap(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); template<class InputIterator> 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()); unordered_multimap(const unordered_multimap& other); unordered_multimap(unordered_multimap&& other); template<class InputIterator> unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a); explicit unordered_multimap(const Allocator& a); unordered_multimap(const unordered_multimap& other, const Allocator& a); unordered_multimap(unordered_multimap&& other, const Allocator& a); unordered_multimap(std::initializer_list<value_type> il, size_type n = implementation-defined, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); unordered_multimap(size_type n, const allocator_type& a); unordered_multimap(size_type n, const hasher& hf, const allocator_type& a); template<class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a); template<class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a); unordered_multimap(std::initializer_list<value_type> il, const allocator_type& a); unordered_multimap(std::initializer_list<value_type> il, size_type n, const allocator_type& a); unordered_multimap(std::initializer_list<value_type> il, size_type n, const hasher& hf, const allocator_type& a); ~unordered_multimap(); unordered_multimap& operator=(const unordered_multimap& other); unordered_multimap& operator=(unordered_multimap&& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value && boost::is_nothrow_move_assignable_v<Hash> && boost::is_nothrow_move_assignable_v<Pred>); unordered_multimap& operator=(std::initializer_list<value_type> il); allocator_type get_allocator() const noexcept; // iterators iterator begin() noexcept; const_iterator begin() const noexcept; iterator end() noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; // capacity [[nodiscard]] bool empty() const noexcept; size_type size() const noexcept; size_type max_size() const noexcept; // modifiers template<class... Args> iterator emplace(Args&&... args); template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args); iterator insert(const value_type& obj); iterator insert(value_type&& obj); template<class P> iterator insert(P&& obj); iterator insert(const_iterator hint, const value_type& obj); iterator insert(const_iterator hint, value_type&& obj); template<class P> iterator insert(const_iterator hint, P&& obj); template<class InputIterator> void insert(InputIterator first, InputIterator last); void insert(std::initializer_list<value_type> il); node_type extract(const_iterator position); node_type extract(const key_type& k); template<class K> node_type extract(K&& k); iterator insert(node_type&& nh); iterator insert(const_iterator hint, node_type&& nh); iterator erase(iterator position); iterator erase(const_iterator position); size_type erase(const key_type& k); template<class K> size_type erase(K&& k); iterator erase(const_iterator first, const_iterator last); void quick_erase(const_iterator position); void erase_return_void(const_iterator position); void swap(unordered_multimap& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value && boost::is_nothrow_swappable_v<Hash> && boost::is_nothrow_swappable_v<Pred>); void clear() noexcept; template<class H2, class P2> void merge(unordered_multimap<Key, T, H2, P2, Allocator>& source); template<class H2, class P2> void merge(unordered_multimap<Key, T, H2, P2, Allocator>&& source); template<class H2, class P2> void merge(unordered_map<Key, T, H2, P2, Allocator>& source); template<class H2, class P2> void merge(unordered_map<Key, T, H2, P2, Allocator>&& source); // observers hasher hash_function() const; key_equal key_eq() const; // map operations iterator find(const key_type& k); const_iterator find(const key_type& k) const; template<class K> iterator find(const K& k); template<class K> const_iterator find(const K& k) const; template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq); template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> const_iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const; size_type count(const key_type& k) const; template<class K> size_type count(const K& k) const; bool contains(const key_type& k) const; template<class K> bool contains(const K& k) const; std::pair<iterator, iterator> equal_range(const key_type& k); std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const; template<class K> std::pair<iterator, iterator> equal_range(const K& k); template<class K> std::pair<const_iterator, const_iterator> equal_range(const K& k) const; // bucket interface size_type bucket_count() const noexcept; size_type max_bucket_count() const noexcept; size_type bucket_size(size_type n) const; size_type bucket(const key_type& k) const; template<class K> size_type bucket(const K& k) const; local_iterator begin(size_type n); const_local_iterator begin(size_type n) const; local_iterator end(size_type n); const_local_iterator end(size_type n) const; const_local_iterator cbegin(size_type n) const; const_local_iterator cend(size_type n) const; // hash policy float load_factor() const noexcept; float max_load_factor() const noexcept; void max_load_factor(float z); void rehash(size_type n); void reserve(size_type n); }; // Deduction Guides template<class InputIterator, class Hash = boost::hash<iter-key-type<InputIterator>>, class Pred = std::equal_to<iter-key-type<InputIterator>>, class Allocator = std::allocator<iter-to-alloc-type<InputIterator>>> unordered_multimap(InputIterator, InputIterator, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_multimap<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, Hash, Pred, Allocator>; template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T>>> unordered_multimap(std::initializer_list<std::pair<Key, T>>, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_multimap<Key, T, Hash, Pred, Allocator>; template<class InputIterator, class Allocator> unordered_multimap(InputIterator, InputIterator, typename see below::size_type, Allocator) -> unordered_multimap<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, boost::hash<iter-key-type<InputIterator>>, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class InputIterator, class Allocator> unordered_multimap(InputIterator, InputIterator, Allocator) -> unordered_multimap<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, boost::hash<iter-key-type<InputIterator>>, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class InputIterator, class Hash, class Allocator> unordered_multimap(InputIterator, InputIterator, typename see below::size_type, Hash, Allocator) -> unordered_multimap<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, Hash, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class Key, class T, class Allocator> unordered_multimap(std::initializer_list<std::pair<Key, T>>, typename see below::size_type, Allocator) -> unordered_multimap<Key, T, boost::hash<Key>, std::equal_to<Key>, Allocator>; template<class Key, class T, class Allocator> unordered_multimap(std::initializer_list<std::pair<Key, T>>, Allocator) -> unordered_multimap<Key, T, boost::hash<Key>, std::equal_to<Key>, Allocator>; template<class Key, class T, class Hash, class Allocator> unordered_multimap(std::initializer_list<std::pair<Key, T>>, typename see below::size_type, Hash, Allocator) -> unordered_multimap<Key, T, Hash, std::equal_to<Key>, Allocator>; // Equality Comparisons template<class Key, class T, class Hash, class Pred, class Alloc> bool operator==(const unordered_multimap<Key, T, Hash, Pred, Alloc>& x, const unordered_multimap<Key, T, Hash, Pred, Alloc>& y); template<class Key, class T, class Hash, class Pred, class Alloc> bool operator!=(const unordered_multimap<Key, T, Hash, Pred, Alloc>& x, const unordered_multimap<Key, T, Hash, Pred, Alloc>& y); // swap template<class Key, class T, class Hash, class Pred, class Alloc> void swap(unordered_multimap<Key, T, Hash, Pred, Alloc>& x, unordered_multimap<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y))); // Erasure template<class K, class T, class H, class P, class A, class Predicate> typename unordered_multimap<K, T, H, P, A>::size_type erase_if(unordered_multimap<K, T, H, P, A>& c, Predicate pred); // Pmr aliases (C++17 and up) namespace unordered::pmr { template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>> using unordered_multimap = boost::unordered_multimap<Key, T, Hash, Pred, std::pmr::polymorphic_allocator<std::pair<const Key, T>>>; } }
描述
模板参数
键 |
|
T |
|
哈希 |
一个一元函数对象类型,充当 |
Pred |
一个二元函数对象,它在 |
分配器 |
一个分配器,其值类型与容器的值类型相同。支持使用花哨指针的分配器。 |
元素被组织成桶。具有相同哈希码的键存储在同一个桶中。
桶的数量可以通过调用插入操作自动增加,或者作为调用重新哈希的结果。
配置宏
BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0
全局定义此宏以支持加载保存到Boost 1.84之前的Boost.Serialization存档中的unordered_multimap
。
类型定义
typedef implementation-defined iterator;
一个迭代器,其值类型为 value_type
。
迭代器类别至少是前向迭代器。
可转换为 const_iterator
。
typedef implementation-defined const_iterator;
一个常量迭代器,其值类型为 value_type
。
迭代器类别至少是前向迭代器。
typedef implementation-defined local_iterator;
一个与迭代器具有相同值类型、差值类型和指针和引用类型的迭代器。
local_iterator
对象可用于遍历单个桶。
typedef implementation-defined const_local_iterator;
一个与 const_iterator
具有相同值类型、差值类型和指针和引用类型的常量迭代器。
const_local_iterator
对象可用于遍历单个桶。
typedef implementation-defined node_type;
详情请参见node_handle_map。
构造函数
默认构造函数
unordered_multimap();
使用 hasher()
作为哈希函数、key_equal()
作为键相等谓词、allocator_type()
作为分配器和 1.0 的最大负载因子来构造一个空容器。
后置条件 |
|
要求 |
如果使用默认值,则 |
桶计数构造函数
explicit unordered_multimap(size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。
后置条件 |
|
要求 |
如果使用默认值,则 |
迭代器范围构造函数
template<class InputIterator>
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());
使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `[f, l)` 区间内的元素插入其中。
要求 |
如果使用默认值,则 |
复制构造函数
unordered_multimap(const unordered_multimap& other);
复制构造函数。复制包含的元素、哈希函数、谓词、最大负载因子和分配器。
如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。
要求 |
`value_type` 可复制构造。 |
移动构造函数
unordered_multimap(unordered_multimap&& other);
移动构造函数。
注释 |
这是使用 Boost.Move 实现的。 |
要求 |
`value_type` 可移动构造。 |
带分配器的迭代器范围构造函数
template<class InputIterator>
unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a);
使用 `a` 作为分配器,使用默认的哈希函数和键相等谓词,最大负载因子为 `1.0`,构造一个空容器,并将 `[f, l)` 区间内的元素插入其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带分配器的复制构造函数
unordered_multimap(const unordered_multimap& other, const Allocator& a);
构造一个容器,复制 `other` 包含的元素、哈希函数、谓词、最大负载因子,但使用分配器 `a`。
带分配器的移动构造函数
unordered_multimap(unordered_multimap&& other, const Allocator& a);
构造一个容器,移动 `other` 包含的元素,并具有哈希函数、谓词和最大负载因子,但使用分配器 `a`。
注释 |
这是使用 Boost.Move 实现的。 |
要求 |
`value_type` 可移动插入。 |
初始化列表构造函数
unordered_multimap(std::initializer_list<value_type> il,
size_type n = implementation-defined,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `il` 中的元素插入其中。
要求 |
如果使用默认值,则 |
带分配器的桶计数构造函数
unordered_multimap(size_type n, const allocator_type& a);
使用 `hf` 作为哈希函数,使用默认的哈希函数和键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。
后置条件 |
|
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带哈希函数和分配器的桶计数构造函数
unordered_multimap(size_type n, const hasher& hf, const allocator_type& a);
使用 `hf` 作为哈希函数,使用默认的键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。
后置条件 |
|
要求 |
`key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的迭代器范围构造函数
template<class InputIterator>
unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a);
使用 `a` 作为分配器,使用默认的哈希函数和键相等谓词,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `[f, l)` 区间内的元素插入其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和哈希函数的迭代器范围构造函数
template<class InputIterator>
unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a);
使用 `hf` 作为哈希函数,`a` 作为分配器,使用默认的键相等谓词,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `[f, l)` 区间内的元素插入其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
带分配器的初始化列表构造函数
unordered_multimap(std::initializer_list<value_type> il, const allocator_type& a);
使用 `a` 作为分配器,最大负载因子为 1.0,构造一个空容器,并将 `il` 中的元素插入其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的初始化列表构造函数
unordered_multimap(std::initializer_list<value_type> il, size_type n, const allocator_type& a);
使用 `a` 作为分配器,最大负载因子为 1.0,构造一个至少包含 `n` 个桶的空容器,并将 `il` 中的元素插入其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数、哈希函数和分配器的初始化列表构造函数
unordered_multimap(std::initializer_list<value_type> il, size_type n, const hasher& hf,
const allocator_type& a);
使用 `hf` 作为哈希函数,`a` 作为分配器,最大负载因子为 1.0,构造一个至少包含 `n` 个桶的空容器,并将 `il` 中的元素插入其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
赋值
复制赋值
unordered_multimap& operator=(const unordered_multimap& other);
赋值运算符。复制包含的元素、哈希函数、谓词和最大负载因子,但不复制分配器。
如果存在 `Alloc::propagate_on_container_copy_assignment` 并且 `Alloc::propagate_on_container_copy_assignment::value` 为 `true`,则分配器将被覆盖;否则,将使用现有分配器创建复制的元素。
要求 |
`value_type` 可复制构造。 |
移动赋值
unordered_multimap& operator=(unordered_multimap&& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
boost::is_nothrow_move_assignable_v<Hash> &&
boost::is_nothrow_move_assignable_v<Pred>);
移动赋值运算符。
如果存在 `Alloc::propagate_on_container_move_assignment` 并且 `Alloc::propagate_on_container_move_assignment::value` 为 `true`,则分配器将被覆盖;否则,将使用现有分配器创建移动的元素。
要求 |
`value_type` 可移动构造。 |
迭代器
begin
iterator begin() noexcept;
const_iterator begin() const noexcept;
返回值 |
指向容器第一个元素的迭代器,如果容器为空,则返回容器的尾后值。 |
修改器
emplace
template<class... Args> iterator emplace(Args&&... args);
使用参数args
构造的对象插入到容器中。
要求 |
`value_type` 可从 `args` 就地构造 到 `X` 中。 |
返回值 |
指向插入元素的迭代器。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
emplace_hint
template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
使用参数args构造的对象插入到容器中。
`position` 是关于应插入元素位置的建议。
要求 |
`value_type` 可从 `args` 就地构造 到 `X` 中。 |
返回值 |
指向插入元素的迭代器。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
复制插入
iterator insert(const value_type& obj);
将obj
插入到容器中。
要求 |
`value_type` 可 复制插入。 |
返回值 |
指向插入元素的迭代器。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
移动插入
iterator insert(value_type&& obj);
将obj
插入到容器中。
要求 |
`value_type` 可 移动插入。 |
返回值 |
指向插入元素的迭代器。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
就地插入
template<class P> iterator insert(P&& obj);
通过执行 `emplace(std::forward<P>(value))` 将元素插入容器中。
只有当 `std::is_constructible<value_type, P&&>::value` 为 `true` 时,才参与重载解析。
返回值 |
指向插入元素的迭代器。 |
带提示的复制插入
iterator insert(const_iterator hint, const value_type& obj);
将obj
插入到容器中。
`hint` 是关于应插入元素位置的建议。
要求 |
`value_type` 可 复制插入。 |
返回值 |
指向插入元素的迭代器。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
带提示的移动插入
iterator insert(const_iterator hint, value_type&& obj);
将obj
插入到容器中。
`hint` 是关于应插入元素位置的建议。
要求 |
`value_type` 可 移动插入。 |
返回值 |
指向插入元素的迭代器。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
带提示的就地插入
template<class P> iterator insert(const_iterator hint, P&& obj);
通过执行 `emplace_hint(hint, std::forward<P>(value))` 将元素插入容器中。
只有当 `std::is_constructible<value_type, P&&>::value` 为 `true` 时,才参与重载解析。
`hint` 是关于应插入元素位置的建议。
返回值 |
指向插入元素的迭代器。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
插入迭代器范围
template<class InputIterator> void insert(InputIterator first, InputIterator last);
将一系列元素插入到容器中。
要求 |
`value_type` 可从 `*first` 就地构造 到 `X` 中。 |
抛出 |
插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
插入初始化列表
void insert(std::initializer_list<value_type> il);
将一系列元素插入到容器中。
要求 |
`value_type` 可 复制插入 到容器中。 |
抛出 |
插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
通过迭代器提取
node_type extract(const_iterator position);
删除 `position` 指向的元素。
返回值 |
拥有该元素的 `node_type`。 |
注释 |
使用此方法提取的节点可以插入到兼容的 |
通过键提取
node_type extract(const key_type& k);
template<class K> node_type extract(K&& k);
移除键值等效于k
的元素。
返回值 |
如果找到,则返回拥有该元素的 |
抛出 |
只有当 |
注释 |
使用此方法提取的节点可以插入到兼容的 只有当 |
使用node_handle
插入
iterator insert(node_type&& nh);
如果nh
为空,则没有效果。
否则插入nh
拥有的元素。
要求 |
|
返回值 |
如果 否则,返回指向新插入元素的迭代器。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 这可以用来插入从兼容的 |
使用提示和node_handle
插入
iterator insert(const_iterator hint, node_type&& nh);
如果nh
为空,则没有效果。
否则插入nh
拥有的元素。
`hint` 是关于应插入元素位置的建议。
要求 |
|
返回值 |
如果 否则,返回指向新插入元素的迭代器。 |
抛出 |
如果由除对hasher的调用以外的操作抛出异常,则该函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 这可以用来插入从兼容的 |
按位置删除
iterator erase(iterator position);
iterator erase(const_iterator position);
删除position
指向的元素。
返回值 |
删除之前 |
抛出 |
只有当 |
注释 |
在旧版本中,这可能效率低下,因为它必须搜索多个桶才能找到返回迭代器的位置。数据结构已更改,因此这种情况不再发生,并且替代删除方法已被弃用。 |
按键删除
size_type erase(const key_type& k);
template<class K> size_type erase(K&& k);
删除所有键值等效于k
的元素。
返回值 |
删除的元素数量。 |
抛出 |
只有当 |
注释 |
只有当 |
删除范围
iterator erase(const_iterator first, const_iterator last);
删除从first
到last
范围内的元素。
返回值 |
删除元素之后的迭代器 - 即 |
抛出 |
只有当 在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。 |
快速删除(quick_erase)
void quick_erase(const_iterator position);
删除position
指向的元素。
抛出 |
只有当 在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。 |
注释 |
实现此方法是因为从erase返回指向下一个元素的迭代器代价很高,但是容器已经重新设计,这种情况不再发生。因此,此方法现已弃用。 |
返回void的删除 (erase_return_void)
void erase_return_void(const_iterator position);
删除position
指向的元素。
抛出 |
只有当 在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。 |
注释 |
实现此方法是因为从erase返回指向下一个元素的迭代器代价很高,但是容器已经重新设计,这种情况不再发生。因此,此方法现已弃用。 |
交换 (swap)
void swap(unordered_multimap& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
boost::is_nothrow_swappable_v<Hash> &&
boost::is_nothrow_swappable_v<Pred>);
将容器的内容与参数交换。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。
抛出 |
除非 |
注释 |
异常规范与C++11标准略有不同,因为相等谓词和哈希函数使用它们的复制构造函数进行交换。 |
合并 (merge)
template<class H2, class P2>
void merge(unordered_multimap<Key, T, H2, P2, Allocator>& source);
template<class H2, class P2>
void merge(unordered_multimap<Key, T, H2, P2, Allocator>&& source);
template<class H2, class P2>
void merge(unordered_map<Key, T, H2, P2, Allocator>& source);
template<class H2, class P2>
void merge(unordered_map<Key, T, H2, P2, Allocator>&& source);
尝试通过迭代source
并将source
中的所有节点提取并插入到*this
中来“合并”两个容器。
因为source
可以具有不同的哈希函数和键相等谓词,所以source
中每个节点的键将使用this->hash_function()
重新哈希,然后根据需要使用this->key_eq()
进行比较。
如果this->get_allocator() != source.get_allocator()
,则此函数的行为未定义。
此函数不复制或移动任何元素,而是简单地将节点从source
重新定位到*this
。
注释 |
|
查找
查找 (find)
iterator find(const key_type& k);
const_iterator find(const key_type& k) const;
template<class K>
iterator find(const K& k);
template<class K>
const_iterator find(const K& k) const;
template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate>
iterator find(CompatibleKey const& k, CompatibleHash const& hash,
CompatiblePredicate const& eq);
template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate>
const_iterator find(CompatibleKey const& k, CompatibleHash const& hash,
CompatiblePredicate const& eq) const;
返回值 |
指向键值等效于 |
注释 |
包含 只有当 |
计数 (count)
size_type count(const key_type& k) const;
template<class K>
size_type count(const K& k) const;
返回值 |
键值等效于 |
注释 |
只有当 |
包含 (contains)
bool contains(const key_type& k) const;
template<class K>
bool contains(const K& k) const;
返回值 |
一个布尔值,指示容器中是否存在键等于 |
注释 |
只有当 |
相等范围 (equal_range)
std::pair<iterator, iterator> equal_range(const key_type& k);
std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
template<class K>
std::pair<iterator, iterator> equal_range(const K& k);
template<class K>
std::pair<const_iterator, const_iterator> equal_range(const K& k) const;
返回值 |
包含所有键值等效于 |
注释 |
只有当 |
桶接口
桶 (bucket)
size_type bucket(const key_type& k) const;
template<class K> size_type bucket(const K& k) const;
返回值 |
将包含键为 |
后置条件 |
返回值小于 |
注释 |
只有当 |
begin
local_iterator begin(size_type n);
const_local_iterator begin(size_type n) const;
要求 |
|
返回值 |
指向索引为 |
end
local_iterator end(size_type n);
const_local_iterator end(size_type n) const;
要求 |
|
返回值 |
指向索引为 |
cbegin
const_local_iterator cbegin(size_type n) const;
要求 |
|
返回值 |
指向索引为 |
cend
const_local_iterator cend(size_type n) const;
要求 |
|
返回值 |
指向索引为 |
哈希策略
重新哈希 (rehash)
void rehash(size_type n);
更改桶的数量,使桶数至少为n
,并且负载因子小于或等于最大负载因子。在适用情况下,这将使与容器关联的bucket_count()
增加或减少。
当size() == 0
时,rehash(0)
将释放底层桶数组。
使迭代器失效,并更改元素的顺序。指向元素的指针和引用不会失效。
抛出 |
除非容器的哈希函数或比较函数抛出异常,否则该函数无效。 |
预留空间 (reserve)
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
类似,此函数可用于增加或减少容器中桶的数量。
使迭代器失效,并更改元素的顺序。指向元素的指针和引用不会失效。
抛出 |
除非容器的哈希函数或比较函数抛出异常,否则该函数无效。 |
推导指南
如果出现以下任何情况,则推导指南不会参与重载解析:
-
它具有
InputIterator
模板参数,并且为该参数推导的类型不符合输入迭代器的资格。 -
它具有
Allocator
模板参数,并且为该参数推导的类型不符合分配器的资格。 -
它具有
Hash
模板参数,并且为该参数推导的类型为整数类型或符合分配器资格的类型。 -
它具有
Pred
模板参数,并且为该参数推导的类型符合分配器资格的类型。
推导指南中的size_type
参数类型引用由推导指南推导出的容器类型的size_type
成员类型。其默认值与所选构造函数的默认值一致。
迭代器值类型 (iter-value-type)
template<class InputIterator> using iter-value-type = typename std::iterator_traits<InputIterator>::value_type; // exposition only
迭代器键类型 (iter-key-type)
template<class InputIterator> using iter-key-type = std::remove_const_t< std::tuple_element_t<0, iter-value-type<InputIterator>>>; // exposition only
迭代器映射类型 (iter-mapped-type)
template<class InputIterator> using iter-mapped-type = std::tuple_element_t<1, iter-value-type<InputIterator>>; // exposition only
迭代器到分配器类型 (iter-to-alloc-type)
template<class InputIterator> using iter-to-alloc-type = std::pair< std::add_const_t<std::tuple_element_t<0, iter-value-type<InputIterator>>>, std::tuple_element_t<1, iter-value-type<InputIterator>>>; // exposition only
相等比较
相等运算符 (operator==)
template<class Key, class T, class Hash, class Pred, class Alloc>
bool operator==(const unordered_multimap<Key, T, Hash, Pred, Alloc>& x,
const unordered_multimap<Key, T, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
并且对于x
中的每个等效键组,在y
中都有一个相同键的组,该组是排列(使用operator==
比较值类型),则返回true
。
注释 |
如果两个容器没有等效的相等谓词,则行为未定义。 |
不相等运算符 (operator!=)
template<class Key, class T, class Hash, class Pred, class Alloc>
bool operator!=(const unordered_multimap<Key, T, Hash, Pred, Alloc>& x,
const unordered_multimap<Key, T, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
并且对于x
中的每个等效键组,在y
中都有一个相同键的组,该组是排列(使用operator==
比较值类型),则返回false
。
注释 |
如果两个容器没有等效的相等谓词,则行为未定义。 |
交换
template<class Key, class T, class Hash, class Pred, class Alloc>
void swap(unordered_multimap<Key, T, Hash, Pred, Alloc>& x,
unordered_multimap<Key, T, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
交换x
和y
的内容。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。
效果 |
|
抛出 |
除非 |
注释 |
异常规范与C++11标准略有不同,因为相等谓词和哈希函数使用它们的复制构造函数进行交换。 |
erase_if
template<class K, class T, class H, class P, class A, class Predicate>
typename unordered_multimap<K, T, H, P, A>::size_type
erase_if(unordered_multimap<K, T, H, P, A>& c, Predicate pred);
遍历容器c
,并移除所有满足提供的谓词返回true
的元素。
返回值 |
被移除元素的数量。 |
注释 |
等价于
|
序列化
可以使用此库提供的API通过Boost.Serialization存档/检索unordered_multimap
。支持常规存档和XML存档。
将unordered_multimap保存到存档
将unordered_multimap
x
的所有元素保存到存档(XML存档)ar
中。
要求 |
|
从存档加载unordered_multimap
删除unordered_multimap
x
中所有预先存在的元素,并从存档(XML存档)ar
中插入原始unordered_multimap
other
保存到ar
读取的存储中的元素的已恢复副本。
要求 |
|
注意 |
如果存档是使用Boost 1.84之前的Boost版本保存的,则必须全局定义配置宏 |
将迭代器/const_iterator保存到存档
将iterator
(const_iterator
)it
的位置信息保存到存档(XML存档)ar
中。it
可以是end()
迭代器。
要求 |
|
从存档加载迭代器/const_iterator
使iterator
(const_iterator
)it
指向原始iterator
(const_iterator
)在存档(XML存档)ar
读取的存储中保存的已恢复位置。
要求 |
如果 |
类模板 unordered_set
boost::unordered_set
— 一个存储唯一值的无序关联容器。
概要
// #include <boost/unordered/unordered_set.hpp> namespace boost { template<class Key, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<Key>> class unordered_set { public: // types using key_type = Key; using value_type = Key; using hasher = Hash; using key_equal = Pred; using allocator_type = Allocator; using pointer = typename std::allocator_traits<Allocator>::pointer; using const_pointer = typename std::allocator_traits<Allocator>::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 unordered_set(); explicit unordered_set(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); template<class InputIterator> 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()); unordered_set(const unordered_set& other); unordered_set(unordered_set&& other); template<class InputIterator> unordered_set(InputIterator f, InputIterator l, const allocator_type& a); explicit unordered_set(const Allocator& a); unordered_set(const unordered_set& other, const Allocator& a); unordered_set(unordered_set&& other, const Allocator& a); unordered_set(std::initializer_list<value_type> il, size_type n = implementation-defined, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); unordered_set(size_type n, const allocator_type& a); unordered_set(size_type n, const hasher& hf, const allocator_type& a); template<class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a); template<class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a); unordered_set(std::initializer_list<value_type> il, const allocator_type& a); unordered_set(std::initializer_list<value_type> il, size_type n, const allocator_type& a); unordered_set(std::initializer_list<value_type> il, size_type n, const hasher& hf, const allocator_type& a); ~unordered_set(); unordered_set& operator=(const unordered_set& other); unordered_set& operator=(unordered_set&& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value && boost::is_nothrow_move_assignable_v<Hash> && boost::is_nothrow_move_assignable_v<Pred>); unordered_set& operator=(std::initializer_list<value_type> il); allocator_type get_allocator() const noexcept; // iterators iterator begin() noexcept; const_iterator begin() const noexcept; iterator end() noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; // capacity [[nodiscard]] bool empty() const noexcept; size_type size() const noexcept; size_type max_size() const noexcept; // modifiers template<class... Args> std::pair<iterator, bool> emplace(Args&&... args); template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args); std::pair<iterator, bool> insert(const value_type& obj); std::pair<iterator, bool> insert(value_type&& obj); template<class K> std::pair<iterator, bool> insert(K&& k); iterator insert(const_iterator hint, const value_type& obj); iterator insert(const_iterator hint, value_type&& obj); template<class K> iterator insert(const_iterator hint, K&& k); template<class InputIterator> void insert(InputIterator first, InputIterator last); void insert(std::initializer_list<value_type>); node_type extract(const_iterator position); node_type extract(const key_type& k); template<class K> node_type extract(K&& k); insert_return_type insert(node_type&& nh); iterator insert(const_iterator hint, node_type&& nh); iterator erase(iterator position); iterator erase(const_iterator position); size_type erase(const key_type& k); template<class K> size_type erase(K&& k); iterator erase(const_iterator first, const_iterator last); void quick_erase(const_iterator position); void erase_return_void(const_iterator position); void swap(unordered_set& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value && boost::is_nothrow_swappable_v<Hash> && boost::is_nothrow_swappable_v<Pred>); void clear() noexcept; template<class H2, class P2> void merge(unordered_set<Key, H2, P2, Allocator>& source); template<class H2, class P2> void merge(unordered_set<Key, H2, P2, Allocator>&& source); template<class H2, class P2> void merge(unordered_multiset<Key, H2, P2, Allocator>& source); template<class H2, class P2> void merge(unordered_multiset<Key, H2, P2, Allocator>&& source); // observers hasher hash_function() const; key_equal key_eq() const; // set operations iterator find(const key_type& k); const_iterator find(const key_type& k) const; template<class K> iterator find(const K& k); template<class K> const_iterator find(const K& k) const; template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq); template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> const_iterator find(CompatibleKey const& k, CompatibleHash const& hash, CompatiblePredicate const& eq) const; size_type count(const key_type& k) const; template<class K> size_type count(const K& k) const; bool contains(const key_type& k) const; template<class K> bool contains(const K& k) const; std::pair<iterator, iterator> equal_range(const key_type& k); std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const; template<class K> std::pair<iterator, iterator> equal_range(const K& k); template<class K> std::pair<const_iterator, const_iterator> equal_range(const K& k) const; // bucket interface size_type bucket_count() const noexcept; size_type max_bucket_count() const noexcept; size_type bucket_size(size_type n) const; size_type bucket(const key_type& k) const; template<class K> size_type bucket(const K& k) const; local_iterator begin(size_type n); const_local_iterator begin(size_type n) const; local_iterator end(size_type n); const_local_iterator end(size_type n) const; const_local_iterator cbegin(size_type n) const; const_local_iterator cend(size_type n) const; // hash policy float load_factor() const noexcept; float max_load_factor() const noexcept; void max_load_factor(float z); void rehash(size_type n); void reserve(size_type n); }; // Deduction Guides template<class InputIterator, class Hash = boost::hash<iter-value-type<InputIterator>>, class Pred = std::equal_to<iter-value-type<InputIterator>>, class Allocator = std::allocator<iter-value-type<InputIterator>>> unordered_set(InputIterator, InputIterator, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_set<iter-value-type<InputIterator>, Hash, Pred, Allocator>; template<class T, class Hash = boost::hash<T>, class Pred = std::equal_to<T>, class Allocator = std::allocator<T>> unordered_set(std::initializer_list<T>, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_set<T, Hash, Pred, Allocator>; template<class InputIterator, class Allocator> unordered_set(InputIterator, InputIterator, typename see below::size_type, Allocator) -> unordered_set<iter-value-type<InputIterator>, boost::hash<iter-value-type<InputIterator>>, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class InputIterator, class Allocator> unordered_set(InputIterator, InputIterator, Allocator) -> unordered_set<iter-value-type<InputIterator>, boost::hash<iter-value-type<InputIterator>>, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class InputIterator, class Hash, class Allocator> unordered_set(InputIterator, InputIterator, typename see below::size_type, Hash, Allocator) -> unordered_set<iter-value-type<InputIterator>, Hash, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class T, class Allocator> unordered_set(std::initializer_list<T>, typename see below::size_type, Allocator) -> unordered_set<T, boost::hash<T>, std::equal_to<T>, Allocator>; template<class T, class Allocator> unordered_set(std::initializer_list<T>, Allocator) -> unordered_set<T, boost::hash<T>, std::equal_to<T>, Allocator>; template<class T, class Hash, class Allocator> unordered_set(std::initializer_list<T>, typename see below::size_type, Hash, Allocator) -> unordered_set<T, Hash, std::equal_to<T>, Allocator>; // Equality Comparisons template<class Key, class Hash, class Pred, class Alloc> bool operator==(const unordered_set<Key, Hash, Pred, Alloc>& x, const unordered_set<Key, Hash, Pred, Alloc>& y); template<class Key, class Hash, class Pred, class Alloc> bool operator!=(const unordered_set<Key, Hash, Pred, Alloc>& x, const unordered_set<Key, Hash, Pred, Alloc>& y); // swap template<class Key, class Hash, class Pred, class Alloc> void swap(unordered_set<Key, Hash, Pred, Alloc>& x, unordered_set<Key, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y))); // Erasure template<class K, class H, class P, class A, class Predicate> typename unordered_set<K, H, P, A>::size_type erase_if(unordered_set<K, H, P, A>& c, Predicate pred); // Pmr aliases (C++17 and up) namespace unordered::pmr { template<class Key, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>> using unordered_set = boost::unordered_set<Key, Hash, Pred, std::pmr::polymorphic_allocator<Key>>; } }
描述
模板参数
键 |
|
哈希 |
一个一元函数对象类型,充当 |
Pred |
一个二元函数对象,它在 |
分配器 |
一个分配器,其值类型与容器的值类型相同。支持使用花哨指针的分配器。 |
元素被组织成桶。具有相同哈希码的键存储在同一个桶中。
桶的数量可以通过调用插入操作自动增加,或者作为调用重新哈希的结果。
配置宏
BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0
全局定义此宏以支持加载保存到Boost 1.84之前的Boost.Serialization存档中的unordered_set
。
类型定义
typedef implementation-defined iterator;
一个常量迭代器,其值类型为 value_type
。
迭代器类别至少是前向迭代器。
可转换为 const_iterator
。
typedef implementation-defined const_iterator;
一个常量迭代器,其值类型为 value_type
。
迭代器类别至少是前向迭代器。
typedef implementation-defined local_iterator;
一个与迭代器具有相同值类型、差值类型和指针和引用类型的迭代器。
local_iterator
对象可用于遍历单个桶。
typedef implementation-defined const_local_iterator;
一个与 const_iterator
具有相同值类型、差值类型和指针和引用类型的常量迭代器。
const_local_iterator
对象可用于遍历单个桶。
typedef implementation-defined node_type;
一个用于保存已提取容器元素的类,模拟NodeHandle。
typedef implementation-defined insert_return_type;
内部类模板的特化
template<class Iterator, class NodeType>
struct insert_return_type // name is exposition only
{
Iterator position;
bool inserted;
NodeType node;
};
其中 Iterator
= iterator
和 NodeType
= node_type
。
构造函数
默认构造函数
unordered_set();
使用 hasher()
作为哈希函数、key_equal()
作为键相等谓词、allocator_type()
作为分配器和 1.0 的最大负载因子来构造一个空容器。
后置条件 |
|
要求 |
如果使用默认值,则 |
桶计数构造函数
explicit unordered_set(size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。
后置条件 |
|
要求 |
如果使用默认值,则 |
迭代器范围构造函数
template<class InputIterator>
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());
使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `[f, l)` 区间内的元素插入其中。
要求 |
如果使用默认值,则 |
复制构造函数
unordered_set(const unordered_set& other);
复制构造函数。复制包含的元素、哈希函数、谓词、最大负载因子和分配器。
如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。
要求 |
`value_type` 可复制构造。 |
带分配器的迭代器范围构造函数
template<class InputIterator>
unordered_set(InputIterator f, InputIterator l, const allocator_type& a);
使用 `a` 作为分配器,使用默认的哈希函数和键相等谓词,最大负载因子为 `1.0`,构造一个空容器,并将 `[f, l)` 区间内的元素插入其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带分配器的复制构造函数
unordered_set(const unordered_set& other, const Allocator& a);
构造一个容器,复制 `other` 包含的元素、哈希函数、谓词、最大负载因子,但使用分配器 `a`。
带分配器的移动构造函数
unordered_set(unordered_set&& other, const Allocator& a);
构造一个容器,移动 `other` 包含的元素,并具有哈希函数、谓词和最大负载因子,但使用分配器 `a`。
注释 |
这是使用 Boost.Move 实现的。 |
要求 |
`value_type` 可移动插入。 |
初始化列表构造函数
unordered_set(std::initializer_list<value_type> il,
size_type n = implementation-defined,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `il` 中的元素插入其中。
要求 |
如果使用默认值,则 |
带分配器的桶计数构造函数
unordered_set(size_type n, const allocator_type& a);
使用 `hf` 作为哈希函数,使用默认的哈希函数和键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。
后置条件 |
|
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带哈希函数和分配器的桶计数构造函数
unordered_set(size_type n, const hasher& hf, const allocator_type& a);
使用 `hf` 作为哈希函数,使用默认的键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。
后置条件 |
|
要求 |
`key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的迭代器范围构造函数
template<class InputIterator>
unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a);
使用 `a` 作为分配器,使用默认的哈希函数和键相等谓词,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `[f, l)` 区间内的元素插入其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和哈希函数的迭代器范围构造函数
template<class InputIterator>
unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a);
使用 `hf` 作为哈希函数,`a` 作为分配器,使用默认的键相等谓词,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `[f, l)` 区间内的元素插入其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
带分配器的初始化列表构造函数
unordered_set(std::initializer_list<value_type> il, const allocator_type& a);
使用 `a` 作为分配器,最大负载因子为 1.0,构造一个空容器,并将 `il` 中的元素插入其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的初始化列表构造函数
unordered_set(std::initializer_list<value_type> il, size_type n, const allocator_type& a);
使用 `a` 作为分配器,最大负载因子为 1.0,构造一个至少包含 `n` 个桶的空容器,并将 `il` 中的元素插入其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数、哈希函数和分配器的初始化列表构造函数
unordered_set(std::initializer_list<value_type> il, size_type n, const hasher& hf,
const allocator_type& a);
使用 `hf` 作为哈希函数,`a` 作为分配器,最大负载因子为 1.0,构造一个至少包含 `n` 个桶的空容器,并将 `il` 中的元素插入其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
赋值
复制赋值
unordered_set& operator=(const unordered_set& other);
赋值运算符。复制包含的元素、哈希函数、谓词和最大负载因子,但不复制分配器。
如果存在 `Alloc::propagate_on_container_copy_assignment` 并且 `Alloc::propagate_on_container_copy_assignment::value` 为 `true`,则分配器将被覆盖;否则,将使用现有分配器创建复制的元素。
要求 |
`value_type` 可复制构造。 |
移动赋值
unordered_set& operator=(unordered_set&& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
boost::is_nothrow_move_assignable_v<Hash> &&
boost::is_nothrow_move_assignable_v<Pred>);
移动赋值运算符。
如果存在 `Alloc::propagate_on_container_move_assignment` 并且 `Alloc::propagate_on_container_move_assignment::value` 为 `true`,则分配器将被覆盖;否则,将使用现有分配器创建移动的元素。
要求 |
`value_type` 可移动构造。 |
迭代器
begin
iterator begin() noexcept;
const_iterator begin() const noexcept;
返回值 |
指向容器第一个元素的迭代器,如果容器为空,则返回容器的尾后值。 |
修改器
emplace
template<class... Args> std::pair<iterator, bool> emplace(Args&&... args);
当且仅当容器中没有具有等效值的元素时,才将使用参数args
构造的对象插入到容器中。
要求 |
`value_type` 可从 `args` 就地构造 到 `X` 中。 |
返回值 |
返回类型的布尔分量如果进行了插入则为 true。 如果进行了插入,则迭代器指向新插入的元素。否则,它指向具有等效值的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
emplace_hint
template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
当且仅当容器中没有具有等效值的元素时,才将使用参数args
构造的对象插入到容器中。
`position` 是关于应插入元素位置的建议。
要求 |
`value_type` 可从 `args` 就地构造 到 `X` 中。 |
返回值 |
如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
复制插入
std::pair<iterator, bool> insert(const value_type& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
要求 |
`value_type` 可 复制插入。 |
返回值 |
返回类型的布尔分量如果进行了插入则为 true。 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
移动插入
std::pair<iterator, bool> insert(value_type&& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
要求 |
`value_type` 可 移动插入。 |
返回值 |
返回类型的布尔分量如果进行了插入则为 true。 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
透明插入
template<class K> std::pair<iterator, bool> insert(K&& k);
当且仅当容器中没有具有等效键的元素时,才将从std::forward<K>(k)
构造的元素插入到容器中。
要求 |
|
返回值 |
返回类型的布尔分量如果进行了插入则为 true。 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 只有当 |
带提示的复制插入
iterator insert(const_iterator hint, const value_type& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
`hint` 是关于应插入元素位置的建议。
要求 |
`value_type` 可 复制插入。 |
返回值 |
如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
带提示的移动插入
iterator insert(const_iterator hint, value_type&& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
`hint` 是关于应插入元素位置的建议。
要求 |
`value_type` 可 移动插入。 |
返回值 |
如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
带提示的透明插入
template<class K> iterator insert(const_iterator hint, K&& k);
当且仅当容器中没有具有等效键的元素时,才将从std::forward<K>(k)
构造的元素插入到容器中。
`hint` 是关于应插入元素位置的建议。
要求 |
|
返回值 |
如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 只有当 |
插入迭代器范围
template<class InputIterator> void insert(InputIterator first, InputIterator last);
将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。
要求 |
`value_type` 可从 `*first` 就地构造 到 `X` 中。 |
抛出 |
插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
插入初始化列表
void insert(std::initializer_list<value_type>);
将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。
要求 |
`value_type` 可 复制插入 到容器中。 |
抛出 |
插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
通过迭代器提取
node_type extract(const_iterator position);
删除 `position` 指向的元素。
返回值 |
拥有该元素的 `node_type`。 |
注释 |
在C++17中,可以使用此方法提取的节点插入到兼容的 |
按值提取
node_type extract(const key_type& k);
template<class K> node_type extract(K&& k);
移除键值等效于k
的元素。
返回值 |
如果找到,则返回拥有该元素的 |
抛出 |
只有当 |
注释 |
在C++17中,可以使用此方法提取的节点插入到兼容的 只有当 |
使用node_handle
插入
insert_return_type insert(node_type&& nh);
如果nh
为空,则没有效果。
否则,当且仅当容器中没有具有等效键的元素时,插入nh
拥有的元素。
要求 |
|
返回值 |
如果 否则,如果已经存在具有等效键的元素,则返回一个 否则,如果插入成功,则返回一个 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 在C++17中,这可以用来插入从兼容的 |
使用提示和node_handle
插入
iterator insert(const_iterator hint, node_type&& nh);
如果nh
为空,则没有效果。
否则,当且仅当容器中没有具有等效键的元素时,插入nh
拥有的元素。
如果容器中已经存在具有等效键的元素,则对nh
没有影响(即nh
仍然包含该节点)。
`hint` 是关于应插入元素位置的建议。
要求 |
|
返回值 |
如果 如果容器中已经存在具有等效键的元素,则返回指向该元素的迭代器。 否则,返回指向新插入元素的迭代器。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 这可以用来插入从兼容的 |
按位置删除
iterator erase(iterator position);
iterator erase(const_iterator position);
删除position
指向的元素。
返回值 |
删除之前 |
抛出 |
只有当 |
注释 |
在旧版本中,这可能效率低下,因为它必须搜索多个桶才能找到返回迭代器的位置。数据结构已更改,因此这种情况不再发生,并且替代删除方法已被弃用。 |
按值删除
size_type erase(const key_type& k);
template<class K> size_type erase(K&& k);
删除所有键值等效于k
的元素。
返回值 |
删除的元素数量。 |
抛出 |
只有当 |
注释 |
只有当 |
删除范围
iterator erase(const_iterator first, const_iterator last);
删除从first
到last
范围内的元素。
返回值 |
删除元素之后的迭代器 - 即 |
抛出 |
只有当 在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。 |
快速删除(quick_erase)
void quick_erase(const_iterator position);
删除position
指向的元素。
抛出 |
只有当 在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。 |
注释 |
实现此方法是因为从erase返回指向下一个元素的迭代器代价很高,但是容器已经重新设计,这种情况不再发生。因此,此方法现已弃用。 |
返回void的删除 (erase_return_void)
void erase_return_void(const_iterator position);
删除position
指向的元素。
抛出 |
只有当 在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。 |
注释 |
实现此方法是因为从erase返回指向下一个元素的迭代器代价很高,但是容器已经重新设计,这种情况不再发生。因此,此方法现已弃用。 |
交换 (swap)
void swap(unordered_set& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
boost::is_nothrow_swappable_v<Hash> &&
boost::is_nothrow_swappable_v<Pred>);
将容器的内容与参数交换。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。
抛出 |
除非 |
注释 |
异常规范与C++11标准略有不同,因为相等谓词和哈希函数使用它们的复制构造函数进行交换。 |
合并 (merge)
template<class H2, class P2>
void merge(unordered_set<Key, H2, P2, Allocator>& source);
template<class H2, class P2>
void merge(unordered_set<Key, H2, P2, Allocator>&& source);
template<class H2, class P2>
void merge(unordered_multiset<Key, H2, P2, Allocator>& source);
template<class H2, class P2>
void merge(unordered_multiset<Key, H2, P2, Allocator>&& source);
尝试通过迭代source
并将source
中不包含在*this
中的任何节点提取出来,然后将其插入到*this
中,来“合并”两个容器。
因为source
可以具有不同的哈希函数和键相等谓词,所以source
中每个节点的键将使用this->hash_function()
重新哈希,然后根据需要使用this->key_eq()
进行比较。
如果this->get_allocator() != source.get_allocator()
,则此函数的行为未定义。
此函数不复制或移动任何元素,而是简单地将节点从source
重新定位到*this
。
注释 |
|
查找
查找 (find)
iterator find(const key_type& k);
const_iterator find(const key_type& k) const;
template<class K>
iterator find(const K& k);
template<class K>
const_iterator find(const K& k) const;
template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate>
iterator find(CompatibleKey const& k, CompatibleHash const& hash,
CompatiblePredicate const& eq);
template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate>
const_iterator find(CompatibleKey const& k, CompatibleHash const& hash,
CompatiblePredicate const& eq) const;
返回值 |
指向键值等效于 |
注释 |
包含 只有当 |
计数 (count)
size_type count(const key_type& k) const;
template<class K>
size_type count(const K& k) const;
返回值 |
键值等效于 |
注释 |
只有当 |
包含 (contains)
bool contains(const key_type& k) const;
template<class K>
bool contains(const K& k) const;
返回值 |
一个布尔值,指示容器中是否存在键等于 |
注释 |
只有当 |
相等范围 (equal_range)
std::pair<iterator, iterator> equal_range(const key_type& k);
std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
template<class K>
std::pair<iterator, iterator> equal_range(const K& k);
template<class K>
std::pair<const_iterator, const_iterator> equal_range(const K& k) const;
返回值 |
包含所有键值等效于 |
注释 |
只有当 |
桶接口
桶 (bucket)
size_type bucket(const key_type& k) const;
template<class K> size_type bucket(const K& k) const;
返回值 |
将包含键为 |
后置条件 |
返回值小于 |
注释 |
只有当 |
begin
local_iterator begin(size_type n);
const_local_iterator begin(size_type n) const;
要求 |
|
返回值 |
指向索引为 |
end
local_iterator end(size_type n);
const_local_iterator end(size_type n) const;
要求 |
|
返回值 |
指向索引为 |
cbegin
const_local_iterator cbegin(size_type n) const;
要求 |
|
返回值 |
指向索引为 |
cend
const_local_iterator cend(size_type n) const;
要求 |
|
返回值 |
指向索引为 |
哈希策略
重新哈希 (rehash)
void rehash(size_type n);
更改桶的数量,使桶数至少为n
,并且负载因子小于或等于最大负载因子。在适用情况下,这将使与容器关联的bucket_count()
增加或减少。
当size() == 0
时,rehash(0)
将释放底层桶数组。
使迭代器失效,并更改元素的顺序。指向元素的指针和引用不会失效。
抛出 |
除非容器的哈希函数或比较函数抛出异常,否则该函数无效。 |
预留空间 (reserve)
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
类似,此函数可用于增加或减少容器中桶的数量。
使迭代器失效,并更改元素的顺序。指向元素的指针和引用不会失效。
抛出 |
除非容器的哈希函数或比较函数抛出异常,否则该函数无效。 |
推导指南
如果出现以下任何情况,则推导指南不会参与重载解析:
-
它具有
InputIterator
模板参数,并且为该参数推导的类型不符合输入迭代器的资格。 -
它具有
Allocator
模板参数,并且为该参数推导的类型不符合分配器的资格。 -
它具有
Hash
模板参数,并且为该参数推导的类型为整数类型或符合分配器资格的类型。 -
它具有
Pred
模板参数,并且为该参数推导的类型符合分配器资格的类型。
推导指南中的size_type
参数类型引用由推导指南推导出的容器类型的size_type
成员类型。其默认值与所选构造函数的默认值一致。
迭代器值类型 (iter-value-type)
template<class InputIterator> using iter-value-type = typename std::iterator_traits<InputIterator>::value_type; // exposition only
相等比较
相等运算符 (operator==)
template<class Key, class Hash, class Pred, class Alloc>
bool operator==(const unordered_set<Key, Hash, Pred, Alloc>& x,
const unordered_set<Key, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个具有相同键和相同值(使用operator==
比较值类型)的元素,则返回true
。
注释 |
如果两个容器没有等效的相等谓词,则行为未定义。 |
不相等运算符 (operator!=)
template<class Key, class Hash, class Pred, class Alloc>
bool operator!=(const unordered_set<Key, Hash, Pred, Alloc>& x,
const unordered_set<Key, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个具有相同键和相同值(使用operator==
比较值类型)的元素,则返回false
。
注释 |
如果两个容器没有等效的相等谓词,则行为未定义。 |
交换
template<class Key, class Hash, class Pred, class Alloc>
void swap(unordered_set<Key, Hash, Pred, Alloc>& x,
unordered_set<Key, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
交换x
和y
的内容。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。
效果 |
|
抛出 |
除非 |
注释 |
异常规范与C++11标准略有不同,因为相等谓词和哈希函数使用它们的复制构造函数进行交换。 |
erase_if
template<class K, class H, class P, class A, class Predicate>
typename unordered_set<K, H, P, A>::size_type
erase_if(unordered_set<K, H, P, A>& c, Predicate pred);
遍历容器c
,并移除所有满足提供的谓词返回true
的元素。
返回值 |
被移除元素的数量。 |
注释 |
等价于
|
序列化
可以使用此库提供的API通过Boost.Serialization存档/检索unordered_set
。支持常规存档和XML存档。
将unordered_set保存到存档
将unordered_set
x
的所有元素保存到存档(XML存档)ar
中。
要求 |
|
从存档加载unordered_set
删除unordered_set
x
中所有预先存在的元素,并从存档(XML存档)ar
中插入原始unordered_set
other
保存到ar
读取的存储中的元素的已恢复副本。
要求 |
|
注意 |
如果存档是使用Boost 1.84之前的Boost版本保存的,则必须全局定义配置宏 |
将迭代器/const_iterator保存到存档
将iterator
(const_iterator
)it
的位置信息保存到存档(XML存档)ar
中。it
可以是end()
迭代器。
要求 |
|
从存档加载迭代器/const_iterator
使iterator
(const_iterator
)it
指向原始iterator
(const_iterator
)在存档(XML存档)ar
读取的存储中保存的已恢复位置。
要求 |
如果 |
类模板 unordered_multiset
boost::unordered_multiset
— 一个存储值的无序关联容器。同一个键可以存储多次。
概要
// #include <boost/unordered/unordered_set.hpp> namespace boost { template<class Key, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<Key>> class unordered_multiset { public: // types using key_type = Key; using value_type = Key; using hasher = Hash; using key_equal = Pred; using allocator_type = Allocator; using pointer = typename std::allocator_traits<Allocator>::pointer; using const_pointer = typename std::allocator_traits<Allocator>::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 unordered_multiset(); explicit unordered_multiset(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); template<class InputIterator> 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()); unordered_multiset(const unordered_multiset& other); unordered_multiset(unordered_multiset&& other); template<class InputIterator> unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a); explicit unordered_multiset(const Allocator& a); unordered_multiset(const unordered_multiset& other, const Allocator& a); unordered_multiset(unordered_multiset&& other, const Allocator& a); unordered_multiset(std::initializer_list<value_type> il, size_type n = implementation-defined, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); unordered_multiset(size_type n, const allocator_type& a); unordered_multiset(size_type n, const hasher& hf, const allocator_type& a); template<class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a); template<class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a); unordered_multiset(std::initializer_list<value_type> il, const allocator_type& a); unordered_multiset(std::initializer_list<value_type> il, size_type n, const allocator_type& a) unordered_multiset(std::initializer_list<value_type> il, size_type n, const hasher& hf, const allocator_type& a); ~unordered_multiset(); unordered_multiset& operator=(const unordered_multiset& other); unordered_multiset& operator=(unordered_multiset&& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value && boost::is_nothrow_move_assignable_v<Hash> && boost::is_nothrow_move_assignable_v<Pred>); unordered_multiset& operator=(std::initializer_list<value_type> il); allocator_type get_allocator() const noexcept; // iterators iterator begin() noexcept; const_iterator begin() const noexcept; iterator end() noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; // capacity [[nodiscard]] bool empty() const noexcept; size_type size() const noexcept; size_type max_size() const noexcept; // modifiers template<class... Args> iterator emplace(Args&&... args); template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args); iterator insert(const value_type& obj); iterator insert(value_type&& obj); iterator insert(const_iterator hint, const value_type& obj); iterator insert(const_iterator hint, value_type&& obj); template<class InputIterator> void insert(InputIterator first, InputIterator last); void insert(std::initializer_list<value_type> il); node_type extract(const_iterator position); node_type extract(const key_type& k); template<class K> node_type extract(K&& k); iterator insert(node_type&& nh); iterator insert(const_iterator hint, node_type&& nh); iterator erase(iterator position); iterator erase(const_iterator position); size_type erase(const key_type& k); template<class K> size_type erase(K&& x); iterator erase(const_iterator first, const_iterator last); void quick_erase(const_iterator position); void erase_return_void(const_iterator position); void swap(unordered_multiset&) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value && boost::is_nothrow_swappable_v<Hash> && boost::is_nothrow_swappable_v<Pred>); void clear() noexcept; template<class H2, class P2> void merge(unordered_multiset<Key, H2, P2, Allocator>& source); template<class H2, class P2> void merge(unordered_multiset<Key, H2, P2, Allocator>&& source); template<class H2, class P2> void merge(unordered_set<Key, H2, P2, Allocator>& source); template<class H2, class P2> void merge(unordered_set<Key, H2, P2, Allocator>&& source); // observers hasher hash_function() const; key_equal key_eq() const; // set operations iterator find(const key_type& k); const_iterator find(const key_type& k) const; template<class K> iterator find(const K& k); template<class K> const_iterator find(const K& k) const; template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> iterator find(CompatibleKey const&, CompatibleHash const&, CompatiblePredicate const&); template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate> const_iterator find(CompatibleKey const&, CompatibleHash const&, CompatiblePredicate const&) const; size_type count(const key_type& k) const; template<class K> size_type count(const K& k) const; bool contains(const key_type& k) const; template<class K> bool contains(const K& k) const; std::pair<iterator, iterator> equal_range(const key_type& k); std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const; template<class K> std::pair<iterator, iterator> equal_range(const K& k); template<class K> std::pair<const_iterator, const_iterator> equal_range(const K& k) const; // bucket interface size_type bucket_count() const noexcept; size_type max_bucket_count() const noexcept; size_type bucket_size(size_type n) const; size_type bucket(const key_type& k) const; template<class K> size_type bucket(const K& k) const; local_iterator begin(size_type n); const_local_iterator begin(size_type n) const; local_iterator end(size_type n); const_local_iterator end(size_type n) const; const_local_iterator cbegin(size_type n) const; const_local_iterator cend(size_type n) const; // hash policy float load_factor() const noexcept; float max_load_factor() const noexcept; void max_load_factor(float z); void rehash(size_type n); void reserve(size_type n); }; // Deduction Guides template<class InputIterator, class Hash = boost::hash<iter-value-type<InputIterator>>, class Pred = std::equal_to<iter-value-type<InputIterator>>, class Allocator = std::allocator<iter-value-type<InputIterator>>> unordered_multiset(InputIterator, InputIterator, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_multiset<iter-value-type<InputIterator>, Hash, Pred, Allocator>; template<class T, class Hash = boost::hash<T>, class Pred = std::equal_to<T>, class Allocator = std::allocator<T>> unordered_multiset(std::initializer_list<T>, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_multiset<T, Hash, Pred, Allocator>; template<class InputIterator, class Allocator> unordered_multiset(InputIterator, InputIterator, typename see below::size_type, Allocator) -> unordered_multiset<iter-value-type<InputIterator>, boost::hash<iter-value-type<InputIterator>>, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class InputIterator, class Allocator> unordered_multiset(InputIterator, InputIterator, Allocator) -> unordered_multiset<iter-value-type<InputIterator>, boost::hash<iter-value-type<InputIterator>>, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class InputIterator, class Hash, class Allocator> unordered_multiset(InputIterator, InputIterator, typename see below::size_type, Hash, Allocator) -> unordered_multiset<iter-value-type<InputIterator>, Hash, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class T, class Allocator> unordered_multiset(std::initializer_list<T>, typename see below::size_type, Allocator) -> unordered_multiset<T, boost::hash<T>, std::equal_to<T>, Allocator>; template<class T, class Allocator> unordered_multiset(std::initializer_list<T>, Allocator) -> unordered_multiset<T, boost::hash<T>, std::equal_to<T>, Allocator>; template<class T, class Hash, class Allocator> unordered_multiset(std::initializer_list<T>, typename see below::size_type, Hash, Allocator) -> unordered_multiset<T, Hash, std::equal_to<T>, Allocator>; // Equality Comparisons template<class Key, class Hash, class Pred, class Alloc> bool operator==(const unordered_multiset<Key, Hash, Pred, Alloc>& x, const unordered_multiset<Key, Hash, Pred, Alloc>& y); template<class Key, class Hash, class Pred, class Alloc> bool operator!=(const unordered_multiset<Key, Hash, Pred, Alloc>& x, const unordered_multiset<Key, Hash, Pred, Alloc>& y); // swap template<class Key, class Hash, class Pred, class Alloc> void swap(unordered_multiset<Key, Hash, Pred, Alloc>& x, unordered_multiset<Key, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y))); // Erasure template<class K, class H, class P, class A, class Predicate> typename unordered_multiset<K, H, P, A>::size_type erase_if(unordered_multiset<K, H, P, A>& c, Predicate pred); // Pmr aliases (C++17 and up) namespace unordered::pmr { template<class Key, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>> using unordered_multiset = boost::unordered_multiset<Key, Hash, Pred, std::pmr::polymorphic_allocator<Key>>; } }
描述
模板参数
键 |
|
哈希 |
一个一元函数对象类型,充当 |
Pred |
一个二元函数对象,它在 |
分配器 |
一个分配器,其值类型与容器的值类型相同。支持使用花哨指针的分配器。 |
元素被组织成桶。具有相同哈希码的键存储在同一个桶中,并且具有等效键的元素彼此相邻存储。
桶的数量可以通过调用插入操作自动增加,或者作为调用重新哈希的结果。
配置宏
BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0
全局定义此宏以支持加载保存到Boost 1.84之前的Boost.Serialization存档中的unordered_multiset
。
类型定义
typedef implementation-defined iterator;
一个常量迭代器,其值类型为 value_type
。
迭代器类别至少是前向迭代器。
可转换为 const_iterator
。
typedef implementation-defined const_iterator;
一个常量迭代器,其值类型为 value_type
。
迭代器类别至少是前向迭代器。
typedef implementation-defined local_iterator;
一个与迭代器具有相同值类型、差值类型和指针和引用类型的迭代器。
local_iterator
对象可用于遍历单个桶。
typedef implementation-defined const_local_iterator;
一个与 const_iterator
具有相同值类型、差值类型和指针和引用类型的常量迭代器。
const_local_iterator
对象可用于遍历单个桶。
typedef implementation-defined node_type;
详情请参见node_handle_set。
构造函数
默认构造函数
unordered_multiset();
使用 hasher()
作为哈希函数、key_equal()
作为键相等谓词、allocator_type()
作为分配器和 1.0 的最大负载因子来构造一个空容器。
后置条件 |
|
要求 |
如果使用默认值,则 |
桶计数构造函数
explicit unordered_multiset(size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。
后置条件 |
|
要求 |
如果使用默认值,则 |
迭代器范围构造函数
template<class InputIterator>
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());
使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `[f, l)` 区间内的元素插入其中。
要求 |
如果使用默认值,则 |
复制构造函数
unordered_multiset(const unordered_multiset& other);
复制构造函数。复制包含的元素、哈希函数、谓词、最大负载因子和分配器。
如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。
要求 |
`value_type` 可复制构造。 |
移动构造函数
unordered_multiset(unordered_multiset&& other);
移动构造函数。
注释 |
这是使用 Boost.Move 实现的。 |
要求 |
`value_type` 可移动构造。 |
带分配器的迭代器范围构造函数
template<class InputIterator>
unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a);
使用 `a` 作为分配器,使用默认的哈希函数和键相等谓词,最大负载因子为 `1.0`,构造一个空容器,并将 `[f, l)` 区间内的元素插入其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带分配器的复制构造函数
unordered_multiset(const unordered_multiset& other, const Allocator& a);
构造一个容器,复制 `other` 包含的元素、哈希函数、谓词、最大负载因子,但使用分配器 `a`。
带分配器的移动构造函数
unordered_multiset(unordered_multiset&& other, const Allocator& a);
构造一个容器,移动 `other` 包含的元素,并具有哈希函数、谓词和最大负载因子,但使用分配器 `a`。
注释 |
这是使用 Boost.Move 实现的。 |
要求 |
`value_type` 可移动插入。 |
初始化列表构造函数
unordered_multiset(std::initializer_list<value_type> il,
size_type n = implementation-defined,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `il` 中的元素插入其中。
要求 |
如果使用默认值,则 |
带分配器的桶计数构造函数
unordered_multiset(size_type n, const allocator_type& a);
使用 `hf` 作为哈希函数,使用默认的哈希函数和键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。
后置条件 |
|
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带哈希函数和分配器的桶计数构造函数
unordered_multiset(size_type n, const hasher& hf, const allocator_type& a);
使用 `hf` 作为哈希函数,使用默认的键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。
后置条件 |
|
要求 |
`key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的迭代器范围构造函数
template<class InputIterator>
unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a);
使用 `a` 作为分配器,使用默认的哈希函数和键相等谓词,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `[f, l)` 区间内的元素插入其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和哈希函数的迭代器范围构造函数
template<class InputIterator>
unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a);
使用 `hf` 作为哈希函数,`a` 作为分配器,使用默认的键相等谓词,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `[f, l)` 区间内的元素插入其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
带分配器的初始化列表构造函数
unordered_multiset(std::initializer_list<value_type> il, const allocator_type& a);
使用 `a` 作为分配器,最大负载因子为 1.0,构造一个空容器,并将 `il` 中的元素插入其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的初始化列表构造函数
unordered_multiset(std::initializer_list<value_type> il, size_type n, const allocator_type& a)
使用 `a` 作为分配器,最大负载因子为 1.0,构造一个至少包含 `n` 个桶的空容器,并将 `il` 中的元素插入其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数、哈希函数和分配器的初始化列表构造函数
unordered_multiset(std::initializer_list<value_type> il, size_type n, const hasher& hf,
const allocator_type& a);
使用 `hf` 作为哈希函数,`a` 作为分配器,最大负载因子为 1.0,构造一个至少包含 `n` 个桶的空容器,并将 `il` 中的元素插入其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
赋值
复制赋值
unordered_multiset& operator=(const unordered_multiset& other);
赋值运算符。复制包含的元素、哈希函数、谓词和最大负载因子,但不复制分配器。
如果存在 `Alloc::propagate_on_container_copy_assignment` 并且 `Alloc::propagate_on_container_copy_assignment::value` 为 `true`,则分配器将被覆盖;否则,将使用现有分配器创建复制的元素。
要求 |
`value_type` 可复制构造。 |
移动赋值
unordered_multiset& operator=(unordered_multiset&& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
boost::is_nothrow_move_assignable_v<Hash> &&
boost::is_nothrow_move_assignable_v<Pred>);
移动赋值运算符。
如果存在 `Alloc::propagate_on_container_move_assignment` 并且 `Alloc::propagate_on_container_move_assignment::value` 为 `true`,则分配器将被覆盖;否则,将使用现有分配器创建移动的元素。
要求 |
`value_type` 可移动构造。 |
迭代器
begin
iterator begin() noexcept;
const_iterator begin() const noexcept;
返回值 |
指向容器第一个元素的迭代器,如果容器为空,则返回容器的尾后值。 |
修改器
emplace
template<class... Args> iterator emplace(Args&&... args);
使用参数args构造的对象插入到容器中。
要求 |
`value_type` 可从 `args` 就地构造 到 `X` 中。 |
返回值 |
指向插入元素的迭代器。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
emplace_hint
template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
使用参数args构造的对象插入到容器中。
`hint` 是关于应插入元素位置的建议。
要求 |
`value_type` 可从 `args` 就地构造 到 `X` 中。 |
返回值 |
指向插入元素的迭代器。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
复制插入
iterator insert(const value_type& obj);
将obj
插入到容器中。
要求 |
`value_type` 可 复制插入。 |
返回值 |
指向插入元素的迭代器。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
移动插入
iterator insert(value_type&& obj);
将obj
插入到容器中。
要求 |
`value_type` 可 移动插入。 |
返回值 |
指向插入元素的迭代器。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
带提示的复制插入
iterator insert(const_iterator hint, const value_type& obj);
将obj
插入到容器中。
`hint` 是关于应插入元素位置的建议。
要求 |
`value_type` 可 复制插入。 |
返回值 |
指向插入元素的迭代器。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
带提示的移动插入
iterator insert(const_iterator hint, value_type&& obj);
将obj
插入到容器中。
`hint` 是关于应插入元素位置的建议。
要求 |
`value_type` 可 移动插入。 |
返回值 |
指向插入元素的迭代器。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
插入迭代器范围
template<class InputIterator> void insert(InputIterator first, InputIterator last);
将一系列元素插入到容器中。
要求 |
`value_type` 可从 `*first` 就地构造 到 `X` 中。 |
抛出 |
插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
插入初始化列表
void insert(std::initializer_list<value_type> il);
将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。
要求 |
`value_type` 可 复制插入 到容器中。 |
抛出 |
插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 |
通过迭代器提取
node_type extract(const_iterator position);
删除 `position` 指向的元素。
返回值 |
拥有该元素的 `node_type`。 |
注释 |
使用此方法提取的节点可以插入到兼容的 |
按值提取
node_type extract(const key_type& k);
template<class K> node_type extract(K&& k);
移除键值等效于k
的元素。
返回值 |
如果找到,则返回拥有该元素的 |
抛出 |
只有当 |
注释 |
使用此方法提取的节点可以插入到兼容的 只有当 |
使用node_handle
插入
iterator insert(node_type&& nh);
如果nh
为空,则没有效果。
否则插入nh
拥有的元素。
要求 |
|
返回值 |
如果 否则,返回指向新插入元素的迭代器。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 这可以用来插入从兼容的 |
使用提示和node_handle
插入
iterator insert(const_iterator hint, node_type&& nh);
如果nh
为空,则没有效果。
否则插入nh
拥有的元素。
`hint` 是关于应插入元素位置的建议。
要求 |
|
返回值 |
如果 否则,返回指向新插入元素的迭代器。 |
抛出 |
如果由除对hasher的调用以外的操作抛出异常,则该函数无效。 |
注释 |
标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。 可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。 指向元素的指针和引用永远不会失效。 这可以用来插入从兼容的 |
按位置删除
iterator erase(iterator position);
iterator erase(const_iterator position);
删除position
指向的元素。
返回值 |
删除之前 |
抛出 |
只有当 |
注释 |
在旧版本中,这可能效率低下,因为它必须搜索多个桶才能找到返回迭代器的位置。数据结构已更改,因此这种情况不再发生,并且替代删除方法已被弃用。 |
按值删除
size_type erase(const key_type& k);
template<class K> size_type erase(K&& x);
删除所有键值等效于k
的元素。
返回值 |
删除的元素数量。 |
抛出 |
只有当 |
注释 |
只有当 |
删除范围
iterator erase(const_iterator first, const_iterator last);
删除从first
到last
范围内的元素。
返回值 |
删除元素之后的迭代器 - 即 |
抛出 |
只有当 在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。 |
快速删除(quick_erase)
void quick_erase(const_iterator position);
删除position
指向的元素。
抛出 |
只有当 在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。 |
注释 |
实现此方法是因为从erase返回指向下一个元素的迭代器代价很高,但是容器已经重新设计,这种情况不再发生。因此,此方法现已弃用。 |
返回void的删除 (erase_return_void)
void erase_return_void(const_iterator position);
删除position
指向的元素。
抛出 |
只有当 在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。 |
注释 |
实现此方法是因为从erase返回指向下一个元素的迭代器代价很高,但是容器已经重新设计,这种情况不再发生。因此,此方法现已弃用。 |
交换 (swap)
void swap(unordered_multiset&)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value &&
boost::is_nothrow_swappable_v<Hash> &&
boost::is_nothrow_swappable_v<Pred>);
将容器的内容与参数交换。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。
抛出 |
除非 |
注释 |
异常规范与C++11标准略有不同,因为相等谓词和哈希函数使用它们的复制构造函数进行交换。 |
合并 (merge)
template<class H2, class P2>
void merge(unordered_multiset<Key, H2, P2, Allocator>& source);
template<class H2, class P2>
void merge(unordered_multiset<Key, H2, P2, Allocator>&& source);
template<class H2, class P2>
void merge(unordered_set<Key, H2, P2, Allocator>& source);
template<class H2, class P2>
void merge(unordered_set<Key, H2, P2, Allocator>&& source);
尝试通过迭代source
并将source
中的所有节点提取并插入到*this
中来“合并”两个容器。
因为source
可以具有不同的哈希函数和键相等谓词,所以source
中每个节点的键将使用this->hash_function()
重新哈希,然后根据需要使用this->key_eq()
进行比较。
如果this->get_allocator() != source.get_allocator()
,则此函数的行为未定义。
此函数不复制或移动任何元素,而是简单地将节点从source
重新定位到*this
。
注释 |
|
查找
查找 (find)
iterator find(const key_type& k);
const_iterator find(const key_type& k) const;
template<class K>
iterator find(const K& k);
template<class K>
const_iterator find(const K& k) const;
template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate>
iterator find(CompatibleKey const&, CompatibleHash const&,
CompatiblePredicate const&);
template<typename CompatibleKey, typename CompatibleHash, typename CompatiblePredicate>
const_iterator find(CompatibleKey const&, CompatibleHash const&,
CompatiblePredicate const&) const;
返回值 |
指向键值等效于 |
注释 |
包含 只有当 |
计数 (count)
size_type count(const key_type& k) const;
template<class K>
size_type count(const K& k) const;
返回值 |
键值等效于 |
注释 |
只有当 |
包含 (contains)
bool contains(const key_type& k) const;
template<class K>
bool contains(const K& k) const;
返回值 |
一个布尔值,指示容器中是否存在键等于 |
注释 |
只有当 |
相等范围 (equal_range)
std::pair<iterator, iterator> equal_range(const key_type& k);
std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
template<class K>
std::pair<iterator, iterator> equal_range(const K& k);
template<class K>
std::pair<const_iterator, const_iterator> equal_range(const K& k) const;
返回值 |
包含所有键值等效于 |
注释 |
只有当 |
桶接口
桶 (bucket)
size_type bucket(const key_type& k) const;
template<class K> size_type bucket(const K& k) const;
返回值 |
将包含键为 |
后置条件 |
返回值小于 |
注释 |
只有当 |
begin
local_iterator begin(size_type n);
const_local_iterator begin(size_type n) const;
要求 |
|
返回值 |
指向索引为 |
end
local_iterator end(size_type n);
const_local_iterator end(size_type n) const;
要求 |
|
返回值 |
指向索引为 |
cbegin
const_local_iterator cbegin(size_type n) const;
要求 |
|
返回值 |
指向索引为 |
cend
const_local_iterator cend(size_type n) const;
要求 |
|
返回值 |
指向索引为 |
哈希策略
重新哈希 (rehash)
void rehash(size_type n);
更改桶的数量,使桶数至少为n
,并且负载因子小于或等于最大负载因子。在适用情况下,这将使与容器关联的bucket_count()
增加或减少。
当size() == 0
时,rehash(0)
将释放底层桶数组。
使迭代器失效,并更改元素的顺序。指向元素的指针和引用不会失效。
抛出 |
除非容器的哈希函数或比较函数抛出异常,否则该函数无效。 |
预留空间 (reserve)
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
类似,此函数可用于增加或减少容器中桶的数量。
使迭代器失效,并更改元素的顺序。指向元素的指针和引用不会失效。
抛出 |
除非容器的哈希函数或比较函数抛出异常,否则该函数无效。 |
推导指南
如果出现以下任何情况,则推导指南不会参与重载解析:
-
它具有
InputIterator
模板参数,并且为该参数推导的类型不符合输入迭代器的资格。 -
它具有
Allocator
模板参数,并且为该参数推导的类型不符合分配器的资格。 -
它具有
Hash
模板参数,并且为该参数推导的类型为整数类型或符合分配器资格的类型。 -
它具有
Pred
模板参数,并且为该参数推导的类型符合分配器资格的类型。
推导指南中的size_type
参数类型引用由推导指南推导出的容器类型的size_type
成员类型。其默认值与所选构造函数的默认值一致。
迭代器值类型 (iter-value-type)
template<class InputIterator> using iter-value-type = typename std::iterator_traits<InputIterator>::value_type; // exposition only
相等比较
相等运算符 (operator==)
template<class Key, class Hash, class Pred, class Alloc>
bool operator==(const unordered_multiset<Key, Hash, Pred, Alloc>& x,
const unordered_multiset<Key, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个具有相同键和相同值(使用operator==
比较值类型)的元素,则返回true
。
注释 |
如果两个容器没有等效的相等谓词,则行为未定义。 |
不相等运算符 (operator!=)
template<class Key, class Hash, class Pred, class Alloc>
bool operator!=(const unordered_multiset<Key, Hash, Pred, Alloc>& x,
const unordered_multiset<Key, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个具有相同键和相同值(使用operator==
比较值类型)的元素,则返回false
。
注释 |
如果两个容器没有等效的相等谓词,则行为未定义。 |
交换
template<class Key, class Hash, class Pred, class Alloc>
void swap(unordered_multiset<Key, Hash, Pred, Alloc>& x,
unordered_multiset<Key, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
交换x
和y
的内容。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。
效果 |
|
抛出 |
除非 |
注释 |
异常规范与C++11标准略有不同,因为相等谓词和哈希函数使用它们的复制构造函数进行交换。 |
erase_if
template<class K, class H, class P, class A, class Predicate>
typename unordered_multiset<K, H, P, A>::size_type
erase_if(unordered_multiset<K, H, P, A>& c, Predicate pred);
遍历容器c
,并移除所有满足提供的谓词返回true
的元素。
返回值 |
被移除元素的数量。 |
注释 |
等价于
|
序列化
可以使用此库提供的API通过Boost.Serialization存档/检索unordered_multiset
。支持常规存档和XML存档。
将unordered_multiset保存到存档
将unordered_multiset
x
的所有元素保存到存档(XML存档)ar
中。
要求 |
|
从存档加载unordered_multiset
删除unordered_multiset
x
中所有预先存在的元素,并从存档(XML存档)ar
中插入原始unordered_multiset
other
保存到ar
读取的存储中的元素的已恢复副本。
要求 |
|
注意 |
如果存档是使用Boost 1.84之前的Boost版本保存的,则必须全局定义配置宏 |
将迭代器/const_iterator保存到存档
将iterator
(const_iterator
)it
的位置信息保存到存档(XML存档)ar
中。it
可以是end()
迭代器。
要求 |
|
从存档加载迭代器/const_iterator
使iterator
(const_iterator
)it
指向原始iterator
(const_iterator
)在存档(XML存档)ar
读取的存储中保存的已恢复位置。
要求 |
如果 |
哈希特性
概要
// #include <boost/unordered/hash_traits.hpp> namespace boost { namespace unordered { template<typename Hash> struct hash_is_avalanching; } // namespace unordered } // namespace boost
hash_is_avalanching
template<typename Hash>
struct hash_is_avalanching;
如果一个哈希函数具有*雪崩效应*,则输入的微小变化会导致返回的哈希码发生很大的变化——理想情况下,输入值表示中翻转一位会导致哈希码的每一位以50%的概率翻转。接近此属性对于开放寻址哈希容器的正常行为至关重要。
hash_is_avalanching<Hash>::value
为
-
false
,如果Hash::is_avalanching
不存在; -
Hash::is_avalanching::value
,如果它存在并且在编译时可转换为bool
; -
true
,如果Hash::is_avalanching
为void
(此用法已弃用); -
否则为非法的。
用户可以通过将合适的is_avalanching
类型定义嵌入到Hash
的定义中,或者直接将hash_is_avalanching<Hash>
特化为包含嵌入式编译时常量value
(设置为true
)的类来声明哈希函数Hash
具有雪崩效应。
如果hash_is_avalanching<Hash>::value
为true
,则开放寻址和并发容器按原样使用提供的哈希函数Hash
;否则,它们会实现一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。
统计
开放寻址和并发容器可以配置为保留受提供的哈希函数质量影响的一些内部操作的运行统计数据。
概要
struct stats-summary-type { double average; double variance; double deviation; }; struct insertion-stats-type { std::size_t count; stats-summary-type probe_length; }; struct lookup-stats-type { std::size_t count; stats-summary-type probe_length; stats-summary-type num_comparisons; }; struct stats-type { insertion-stats-type insertion; lookup-stats-type successful_lookup, unsuccessful_lookup; };
stats-summary-type
提供一系列数值的平均值、方差和标准差。
insertion-stats-type
提供容器执行的插入操作次数以及相关*探测长度*(每次操作访问的桶组数量)的统计数据。
lookup-stats-type
对于成功(找到元素)或不成功(未找到)查找,提供容器执行的操作次数以及相关的*探测长度*(访问的桶组数量)和每次操作的元素比较次数的统计数据。
stats-type
提供容器执行的插入、成功和不成功查找的统计数据。如果提供的哈希函数质量良好,则
-
平均探测长度应接近1.0。
-
对于成功的查找,平均元素比较次数应接近1.0。
-
对于不成功的查找,平均元素比较次数应接近0.0。
这些统计数据可用于确定是否可以将给定的哈希函数标记为雪崩。
类模板 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
基本上可以作为基于节点的标准无序关联容器的直接替换。
概要
// #include <boost/unordered/unordered_flat_map.hpp> namespace boost { template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T>>> class unordered_flat_map { public: // types using key_type = Key; using mapped_type = T; using value_type = std::pair<const Key, T>; using init_type = std::pair< typename std::remove_const<Key>::type, typename std::remove_const<T>::type >; using hasher = Hash; using key_equal = Pred; using allocator_type = Allocator; using pointer = typename std::allocator_traits<Allocator>::pointer; using const_pointer = typename std::allocator_traits<Allocator>::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 = stats-type; // if statistics are enabled // construct/copy/destroy unordered_flat_map(); explicit unordered_flat_map(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); template<class InputIterator> 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()); unordered_flat_map(const unordered_flat_map& other); unordered_flat_map(unordered_flat_map&& other); template<class InputIterator> unordered_flat_map(InputIterator f, InputIterator l, const allocator_type& a); explicit unordered_flat_map(const Allocator& a); unordered_flat_map(const unordered_flat_map& other, const Allocator& a); unordered_flat_map(unordered_flat_map&& other, const Allocator& a); unordered_flat_map(concurrent_flat_map<Key, T, Hash, Pred, Allocator>&& other); unordered_flat_map(std::initializer_list<value_type> il, size_type n = implementation-defined const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); unordered_flat_map(size_type n, const allocator_type& a); unordered_flat_map(size_type n, const hasher& hf, const allocator_type& a); template<class InputIterator> unordered_flat_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a); template<class InputIterator> unordered_flat_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a); unordered_flat_map(std::initializer_list<value_type> il, const allocator_type& a); unordered_flat_map(std::initializer_list<value_type> il, size_type n, const allocator_type& a); unordered_flat_map(std::initializer_list<value_type> il, size_type n, const hasher& hf, const allocator_type& a); ~unordered_flat_map(); unordered_flat_map& operator=(const unordered_flat_map& other); unordered_flat_map& operator=(unordered_flat_map&& other) noexcept( (boost::allocator_traits<Allocator>::is_always_equal::value || boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) && std::is_same<pointer, value_type*>::value); unordered_flat_map& operator=(std::initializer_list<value_type>); allocator_type get_allocator() const noexcept; // iterators iterator begin() noexcept; const_iterator begin() const noexcept; iterator end() noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; // capacity [[nodiscard]] bool empty() const noexcept; size_type size() const noexcept; size_type max_size() const noexcept; // modifiers template<class... Args> std::pair<iterator, bool> emplace(Args&&... args); template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args); std::pair<iterator, bool> insert(const value_type& obj); std::pair<iterator, bool> insert(const init_type& obj); std::pair<iterator, bool> insert(value_type&& obj); std::pair<iterator, bool> insert(init_type&& obj); iterator insert(const_iterator hint, const value_type& obj); iterator insert(const_iterator hint, const init_type& obj); iterator insert(const_iterator hint, value_type&& obj); iterator insert(const_iterator hint, init_type&& obj); template<class InputIterator> void insert(InputIterator first, InputIterator last); void insert(std::initializer_list<value_type>); template<class... Args> std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args); template<class... Args> std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args); template<class K, class... Args> std::pair<iterator, bool> try_emplace(K&& k, Args&&... args); template<class... Args> iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); template<class... Args> iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); template<class K, class... Args> iterator try_emplace(const_iterator hint, K&& k, Args&&... args); template<class M> std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj); template<class M> std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj); template<class K, class M> std::pair<iterator, bool> insert_or_assign(K&& k, M&& obj); template<class M> iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); template<class M> iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); template<class K, class M> iterator insert_or_assign(const_iterator hint, K&& k, M&& obj); convertible-to-iterator erase(iterator position); convertible-to-iterator erase(const_iterator position); size_type erase(const key_type& k); template<class K> size_type erase(K&& k); iterator erase(const_iterator first, const_iterator last); void swap(unordered_flat_map& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || boost::allocator_traits<Allocator>::propagate_on_container_swap::value); void clear() noexcept; template<class H2, class P2> void merge(unordered_flat_map<Key, T, H2, P2, Allocator>& source); template<class H2, class P2> void merge(unordered_flat_map<Key, T, H2, P2, Allocator>&& source); // observers hasher hash_function() const; key_equal key_eq() const; // map operations iterator find(const key_type& k); const_iterator find(const key_type& k) const; template<class K> iterator find(const K& k); template<class K> const_iterator find(const K& k) const; size_type count(const key_type& k) const; template<class K> size_type count(const K& k) const; bool contains(const key_type& k) const; template<class K> bool contains(const K& k) const; std::pair<iterator, iterator> equal_range(const key_type& k); std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const; template<class K> std::pair<iterator, iterator> equal_range(const K& k); template<class K> std::pair<const_iterator, const_iterator> equal_range(const K& k) const; // element access mapped_type& operator[](const key_type& k); mapped_type& operator[](key_type&& k); template<class K> mapped_type& operator[](K&& k); mapped_type& at(const key_type& k); const mapped_type& at(const key_type& k) const; template<class K> mapped_type& at(const K& k); template<class K> const mapped_type& at(const K& k) const; // bucket interface size_type bucket_count() const noexcept; // hash policy float load_factor() const noexcept; float max_load_factor() const noexcept; void max_load_factor(float z); size_type max_load() const noexcept; void rehash(size_type n); void reserve(size_type n); // statistics (if enabled) stats get_stats() const; void reset_stats() noexcept; }; // Deduction Guides template<class InputIterator, class Hash = boost::hash<iter-key-type<InputIterator>>, class Pred = std::equal_to<iter-key-type<InputIterator>>, class Allocator = std::allocator<iter-to-alloc-type<InputIterator>>> unordered_flat_map(InputIterator, InputIterator, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_flat_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, Hash, Pred, Allocator>; template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T>>> unordered_flat_map(std::initializer_list<std::pair<Key, T>>, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_flat_map<Key, T, Hash, Pred, Allocator>; template<class InputIterator, class Allocator> unordered_flat_map(InputIterator, InputIterator, typename see below::size_type, Allocator) -> unordered_flat_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, boost::hash<iter-key-type<InputIterator>>, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class InputIterator, class Allocator> unordered_flat_map(InputIterator, InputIterator, Allocator) -> unordered_flat_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, boost::hash<iter-key-type<InputIterator>>, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class InputIterator, class Hash, class Allocator> unordered_flat_map(InputIterator, InputIterator, typename see below::size_type, Hash, Allocator) -> unordered_flat_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, Hash, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class Key, class T, class Allocator> unordered_flat_map(std::initializer_list<std::pair<Key, T>>, typename see below::size_type, Allocator) -> unordered_flat_map<Key, T, boost::hash<Key>, std::equal_to<Key>, Allocator>; template<class Key, class T, class Allocator> unordered_flat_map(std::initializer_list<std::pair<Key, T>>, Allocator) -> unordered_flat_map<Key, T, boost::hash<Key>, std::equal_to<Key>, Allocator>; template<class Key, class T, class Hash, class Allocator> unordered_flat_map(std::initializer_list<std::pair<Key, T>>, typename see below::size_type, Hash, Allocator) -> unordered_flat_map<Key, T, Hash, std::equal_to<Key>, Allocator>; // Equality Comparisons template<class Key, class T, class Hash, class Pred, class Alloc> bool operator==(const unordered_flat_map<Key, T, Hash, Pred, Alloc>& x, const unordered_flat_map<Key, T, Hash, Pred, Alloc>& y); template<class Key, class T, class Hash, class Pred, class Alloc> bool operator!=(const unordered_flat_map<Key, T, Hash, Pred, Alloc>& x, const unordered_flat_map<Key, T, Hash, Pred, Alloc>& y); // swap template<class Key, class T, class Hash, class Pred, class Alloc> void swap(unordered_flat_map<Key, T, Hash, Pred, Alloc>& x, unordered_flat_map<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y))); // Erasure template<class K, class T, class H, class P, class A, class Predicate> typename unordered_flat_map<K, T, H, P, A>::size_type erase_if(unordered_flat_map<K, T, H, P, A>& c, Predicate pred); // Pmr aliases (C++17 and up) namespace unordered::pmr { template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>> using unordered_flat_map = boost::unordered_flat_map<Key, T, Hash, Pred, std::pmr::polymorphic_allocator<std::pair<const Key, T>>>; } }
描述
模板参数
键 |
|
T |
|
哈希 |
一个一元函数对象类型,充当 |
Pred |
一个二元函数对象,它在 |
分配器 |
一个分配器,其值类型与容器的值类型相同。支持使用花哨指针的分配器。 |
容器的元素保存在内部的桶数组中。元素插入到由其哈希码确定的桶中,但如果桶已被占用(冲突),则使用原始位置附近的可用桶。
桶数组的大小可以通过调用insert
/emplace
自动增加,或者通过调用rehash
/reserve
来增加。容器的负载因子(元素数量除以桶的数量)永远不会大于max_load_factor()
,除了在小型尺寸下,实现可能决定允许更高的负载。
如果hash_is_avalanching<Hash>::value
为true
,则哈希函数按原样使用;否则,会添加一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。
类型定义
typedef implementation-defined iterator;
一个迭代器,其值类型为 value_type
。
迭代器类别至少是前向迭代器。
可转换为 const_iterator
。
typedef implementation-defined const_iterator;
一个常量迭代器,其值类型为 value_type
。
迭代器类别至少是前向迭代器。
构造函数
默认构造函数
unordered_flat_map();
使用hasher()
作为哈希函数、key_equal()
作为键相等谓词和allocator_type()
作为分配器来构造一个空容器。
后置条件 |
|
要求 |
如果使用默认值,则 |
桶计数构造函数
explicit unordered_flat_map(size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用hf
作为哈希函数、eql
作为键相等谓词和a
作为分配器来构造一个至少包含n
个桶的空容器。
后置条件 |
|
要求 |
如果使用默认值,则 |
迭代器范围构造函数
template<class InputIterator>
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());
使用hf
作为哈希函数、eql
作为键相等谓词和a
作为分配器来构造一个至少包含n
个桶的空容器,并将[f, l)
中的元素插入到其中。
要求 |
如果使用默认值,则 |
复制构造函数
unordered_flat_map(unordered_flat_map const& other);
复制构造函数。复制包含的元素、哈希函数、谓词和分配器。
如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。
要求 |
`value_type` 可复制构造。 |
移动构造函数
unordered_flat_map(unordered_flat_map&& other);
移动构造函数。other
的内部桶数组直接转移到新容器。哈希函数、谓词和分配器是从other
移动构造的。如果启用了统计数据,则从other
转移内部统计信息并调用other.reset_stats()
。
带分配器的迭代器范围构造函数
template<class InputIterator>
unordered_flat_map(InputIterator f, InputIterator l, const allocator_type& a);
使用a
作为分配器、使用默认的哈希函数和键相等谓词来构造一个空容器,并将[f, l)
中的元素插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带分配器的复制构造函数
unordered_flat_map(unordered_flat_map const& other, Allocator const& a);
构造一个容器,复制other
包含的元素、哈希函数和谓词,但使用分配器a
。
带分配器的移动构造函数
unordered_flat_map(unordered_flat_map&& other, Allocator const& a);
如果a == other.get_allocator()
,则other
的元素直接转移到新容器;否则,元素是从other
的元素移动构造的。哈希函数和谓词是从other
移动构造的,分配器是从a
复制构造的。如果启用了统计数据,则当且仅当最终分配器等于other.get_allocator()
时,才从other
转移内部统计信息,并且始终调用other.reset_stats()
。
从concurrent_flat_map移动构造
unordered_flat_map(concurrent_flat_map<Key, T, Hash, Pred, Allocator>&& other);
从concurrent_flat_map
移动构造。other
的内部桶数组直接转移到新容器。哈希函数、谓词和分配器是从other
移动构造的。如果启用了统计数据,则从other
转移内部统计信息并调用other.reset_stats()
。
复杂度 |
常数时间。 |
并发性 |
阻塞 |
初始化列表构造函数
unordered_flat_map(std::initializer_list<value_type> il,
size_type n = implementation-defined
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用hf
作为哈希函数、eql
作为键相等谓词和a
来构造一个至少包含n
个桶的空容器,并将il
中的元素插入到其中。
要求 |
如果使用默认值,则 |
带分配器的桶计数构造函数
unordered_flat_map(size_type n, allocator_type const& a);
使用hf
作为哈希函数、默认的哈希函数和键相等谓词以及a
作为分配器来构造一个至少包含n
个桶的空容器。
后置条件 |
|
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带哈希函数和分配器的桶计数构造函数
unordered_flat_map(size_type n, hasher const& hf, allocator_type const& a);
使用hf
作为哈希函数、默认的键相等谓词和a
作为分配器来构造一个至少包含n
个桶的空容器。
后置条件 |
|
要求 |
`key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的迭代器范围构造函数
template<class InputIterator>
unordered_flat_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a);
使用a
作为分配器以及默认的哈希函数和键相等谓词来构造一个至少包含n
个桶的空容器,并将[f, l)
中的元素插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和哈希函数的迭代器范围构造函数
template<class InputIterator>
unordered_flat_map(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a);
使用hf
作为哈希函数、a
作为分配器以及默认的键相等谓词来构造一个至少包含n
个桶的空容器,并将[f, l)
中的元素插入到其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
带分配器的初始化列表构造函数
unordered_flat_map(std::initializer_list<value_type> il, const allocator_type& a);
使用a
以及默认的哈希函数和键相等谓词来构造一个空容器,并将il
中的元素插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的初始化列表构造函数
unordered_flat_map(std::initializer_list<value_type> il, size_type n, const allocator_type& a);
使用a
以及默认的哈希函数和键相等谓词来构造一个至少包含n
个桶的空容器,并将il
中的元素插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数、哈希函数和分配器的初始化列表构造函数
unordered_flat_map(std::initializer_list<value_type> il, size_type n, const hasher& hf,
const allocator_type& a);
使用hf
作为哈希函数、a
作为分配器以及默认的键相等谓词来构造一个至少包含n
个桶的空容器,并将il
中的元素插入到其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
赋值
复制赋值
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
元素的副本。
要求 |
|
移动赋值
unordered_flat_map& operator=(unordered_flat_map&& other)
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
std::is_same<pointer, value_type*>::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
转移内部统计信息,并且始终调用other.reset_stats()
。
迭代器
begin
iterator begin() noexcept;
const_iterator begin() const noexcept;
返回值 |
指向容器第一个元素的迭代器,如果容器为空,则返回容器的尾后值。 |
复杂度 |
O( |
cbegin
const_iterator cbegin() const noexcept;
返回值 |
指向容器第一个元素的 `const_iterator`,如果容器为空,则返回容器的尾后值。 |
复杂度 |
O( |
修改器
emplace
template<class... Args> std::pair<iterator, bool> emplace(Args&&... args);
当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。
要求 |
|
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 如果 |
emplace_hint
template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。
position
是关于应插入元素位置的建议。此实现忽略它。
要求 |
|
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 如果 |
复制插入
std::pair<iterator, bool> insert(const value_type& obj);
std::pair<iterator, bool> insert(const init_type& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
要求 |
`value_type` 可 复制插入。 |
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 当 |
移动插入
std::pair<iterator, bool> insert(value_type&& obj);
std::pair<iterator, bool> insert(init_type&& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
要求 |
`value_type` 可 移动插入。 |
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 当 |
带提示的复制插入
iterator insert(const_iterator hint, const value_type& obj);
iterator insert(const_iterator hint, const init_type& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
hint
是关于应插入元素位置的建议。此实现忽略它。
要求 |
`value_type` 可 复制插入。 |
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 当 |
带提示的移动插入
iterator insert(const_iterator hint, value_type&& obj);
iterator insert(const_iterator hint, init_type&& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
hint
是关于应插入元素位置的建议。此实现忽略它。
要求 |
`value_type` 可 移动插入。 |
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 当 |
插入迭代器范围
template<class InputIterator> void insert(InputIterator first, InputIterator last);
将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。
要求 |
|
抛出 |
插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 |
插入初始化列表
void insert(std::initializer_list<value_type>);
将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。
要求 |
`value_type` 可 复制插入 到容器中。 |
抛出 |
插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 |
try_emplace
template<class... Args>
std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args);
template<class... Args>
std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args);
template<class K, class... Args>
std::pair<iterator, bool> try_emplace(K&& k, Args&&... args);
如果容器中不存在具有键 `k` 的元素,则将新元素插入容器中。
如果存在具有键 `k` 的元素,则此函数不执行任何操作。
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
此函数类似于 emplace,不同之处在于,如果存在具有等效键的元素,则不会构造
不同于 emplace,它只是将所有参数转发到 可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 `template<class K, class... Args>` 重载只有在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义,并且 `iterator` 和 `const_iterator` 都不可以隐式转换为 `K` 时才参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这使得异构查找能够避免实例化 `Key` 类型实例的成本。 |
带提示的 try_emplace
template<class... Args>
iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args);
template<class... Args>
iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args);
template<class K, class... Args>
iterator try_emplace(const_iterator hint, K&& k, Args&&... args);
如果容器中不存在具有键 `k` 的元素,则将新元素插入容器中。
如果存在具有键 `k` 的元素,则此函数不执行任何操作。
hint
是关于应插入元素位置的建议。此实现忽略它。
返回值 |
如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
此函数类似于 emplace_hint,不同之处在于,如果存在具有等效键的元素,则不会构造
不同于 emplace_hint,它只是将所有参数转发到 可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 `template<class K, class... Args>` 重载只有在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义,并且 `iterator` 和 `const_iterator` 都不可以隐式转换为 `K` 时才参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这使得异构查找能够避免实例化 `Key` 类型实例的成本。 |
insert_or_assign
template<class M>
std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj);
template<class M>
std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj);
template<class K, class M>
std::pair<iterator, bool> insert_or_assign(K&& k, M&& obj);
将新元素插入容器中,或通过赋值给包含的值来更新现有元素。
如果存在具有键 `k` 的元素,则通过赋值 `std::forward<M>(obj)` 来更新它。
如果没有这样的元素,则将其添加到容器中,如下所示:
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 `template<class K, class M>` 只有在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义时才参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这使得异构查找能够避免实例化 `Key` 类型实例的成本。 |
带提示的 insert_or_assign
template<class M>
iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj);
template<class M>
iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj);
template<class K, class M>
iterator insert_or_assign(const_iterator hint, K&& k, M&& obj);
将新元素插入容器中,或通过赋值给包含的值来更新现有元素。
如果存在具有键 `k` 的元素,则通过赋值 `std::forward<M>(obj)` 来更新它。
如果没有这样的元素,则将其添加到容器中,如下所示:
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
hint
是关于应插入元素位置的建议。此实现忽略它。
返回值 |
如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 `template<class K, class M>` 只有在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义时才参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这使得异构查找能够避免实例化 `Key` 类型实例的成本。 |
按位置删除
convertible-to-iterator erase(iterator position);
convertible-to-iterator erase(const_iterator position);
删除position
指向的元素。
返回值 |
一个不透明对象,可以隐式转换为擦除之前紧跟在 |
抛出 |
无。 |
注释 |
返回的不透明对象只能被丢弃或立即转换为 |
按键删除
size_type erase(const key_type& k);
template<class K> size_type erase(K&& k);
删除所有键值等效于k
的元素。
返回值 |
删除的元素数量。 |
抛出 |
只有当 |
注释 |
只有当 |
删除范围
iterator erase(const_iterator first, const_iterator last);
删除从first
到last
范围内的元素。
返回值 |
删除元素之后的迭代器 - 即 |
抛出 |
此实现中没有任何操作(既不调用 |
交换 (swap)
void swap(unordered_flat_map& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_swap::value);
将容器的内容与参数交换。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。
抛出 |
除非 |
清空 (clear)
void clear() noexcept;
删除容器中的所有元素。
后置条件 |
|
合并 (merge)
template<class H2, class P2>
void merge(unordered_flat_map<Key, T, H2, P2, Allocator>& source);
template<class H2, class P2>
void merge(unordered_flat_map<Key, T, H2, P2, Allocator>&& source);
将 source
中所有键在 *this
中不存在的元素移动插入到 *this
中,并从 source
中删除这些元素。
查找
查找 (find)
iterator find(const key_type& k);
const_iterator find(const key_type& k) const;
template<class K>
iterator find(const K& k);
返回值 |
指向具有与 |
注释 |
只有当 |
计数 (count)
size_type count(const key_type& k) const;
template<class K>
size_type count(const K& k) const;
返回值 |
键值等效于 |
注释 |
只有当 |
包含 (contains)
bool contains(const key_type& k) const;
template<class K>
bool contains(const K& k) const;
返回值 |
一个布尔值,指示容器中是否存在键等于 |
注释 |
只有当 |
相等范围 (equal_range)
std::pair<iterator, iterator> equal_range(const key_type& k);
std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
template<class K>
std::pair<iterator, iterator> equal_range(const K& k);
template<class K>
std::pair<const_iterator, const_iterator> equal_range(const K& k) const;
返回值 |
包含所有键值等效于 |
注释 |
只有当 |
下标运算符 (operator[])
mapped_type& operator[](const key_type& k);
mapped_type& operator[](key_type&& k);
template<class K> mapped_type& operator[](K&& k);
效果 |
如果容器还不包含具有与 |
返回值 |
|
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 只有当 |
at方法
mapped_type& at(const key_type& k);
const mapped_type& at(const key_type& k) const;
template<class K> mapped_type& at(const K& k);
template<class K> const mapped_type& at(const K& k) const;
返回值 |
|
抛出 |
如果不存在这样的元素,则抛出类型为 |
注释 |
只有当 |
哈希策略
负载因子 (load_factor)
float load_factor() const noexcept;
返回值 |
|
设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果 |
不执行任何操作,因为不允许用户更改此参数。为了与 |
最大负载
size_type max_load() const noexcept;
返回值 |
容器在不重新哈希的情况下可以容纳的最大元素数量,假设不再删除元素。 |
注意 |
构造、重新哈希或清除后,容器的最大负载至少为 |
重新哈希 (rehash)
void rehash(size_type n);
如有必要,更改桶数组的大小,使其至少包含 n
个桶,并且负载因子小于或等于最大负载因子。在适用情况下,这将扩大或缩小与容器关联的 bucket_count()
。
当 size() == 0
时,rehash(0)
将释放底层桶数组。如果提供的分配器使用高级指针,则随后将执行默认分配。
使迭代器、指针和引用失效,并更改元素的顺序。
抛出 |
除非容器的哈希函数或比较函数抛出异常,否则该函数无效。 |
预留空间 (reserve)
void reserve(size_type n);
等效于 a.rehash(ceil(n / a.max_load_factor()))
。
与rehash
类似,此函数可用于增加或减少容器中桶的数量。
使迭代器、指针和引用失效,并更改元素的顺序。
抛出 |
除非容器的哈希函数或比较函数抛出异常,否则该函数无效。 |
推导指南
如果出现以下任何情况,则推导指南不会参与重载解析:
-
它具有
InputIterator
模板参数,并且为该参数推导的类型不符合输入迭代器的资格。 -
它具有
Allocator
模板参数,并且为该参数推导的类型不符合分配器的资格。 -
它具有
Hash
模板参数,并且为该参数推导的类型为整数类型或符合分配器资格的类型。 -
它具有
Pred
模板参数,并且为该参数推导的类型符合分配器资格的类型。
推导指南中的size_type
参数类型引用由推导指南推导出的容器类型的size_type
成员类型。其默认值与所选构造函数的默认值一致。
迭代器值类型 (iter-value-type)
template<class InputIterator> using iter-value-type = typename std::iterator_traits<InputIterator>::value_type; // exposition only
迭代器键类型 (iter-key-type)
template<class InputIterator> using iter-key-type = std::remove_const_t< std::tuple_element_t<0, iter-value-type<InputIterator>>>; // exposition only
迭代器映射类型 (iter-mapped-type)
template<class InputIterator> using iter-mapped-type = std::tuple_element_t<1, iter-value-type<InputIterator>>; // exposition only
迭代器到分配器类型 (iter-to-alloc-type)
template<class InputIterator> using iter-to-alloc-type = std::pair< std::add_const_t<std::tuple_element_t<0, iter-value-type<InputIterator>>>, std::tuple_element_t<1, iter-value-type<InputIterator>>>; // exposition only
相等比较
相等运算符 (operator==)
template<class Key, class T, class Hash, class Pred, class Alloc>
bool operator==(const unordered_flat_map<Key, T, Hash, Pred, Alloc>& x,
const unordered_flat_map<Key, T, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个具有相同键和相同值(使用operator==
比较值类型)的元素,则返回true
。
注释 |
如果两个容器没有等效的相等谓词,则行为未定义。 |
不相等运算符 (operator!=)
template<class Key, class T, class Hash, class Pred, class Alloc>
bool operator!=(const unordered_flat_map<Key, T, Hash, Pred, Alloc>& x,
const unordered_flat_map<Key, T, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个具有相同键和相同值(使用operator==
比较值类型)的元素,则返回false
。
注释 |
如果两个容器没有等效的相等谓词,则行为未定义。 |
交换
template<class Key, class T, class Hash, class Pred, class Alloc>
void swap(unordered_flat_map<Key, T, Hash, Pred, Alloc>& x,
unordered_flat_map<Key, T, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
交换x
和y
的内容。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。
效果 |
|
抛出 |
除非 |
erase_if
template<class K, class T, class H, class P, class A, class Predicate>
typename unordered_flat_map<K, T, H, P, A>::size_type
erase_if(unordered_flat_map<K, T, H, P, A>& c, Predicate pred);
遍历容器c
,并移除所有满足提供的谓词返回true
的元素。
返回值 |
被移除元素的数量。 |
注释 |
等价于
|
序列化
可以使用此库提供的 API 通过 Boost.Serialization 存档/检索 unordered_flat_map
。支持常规存档和 XML 存档。
将 unordered_flat_map 保存到存档
将 unordered_flat_map
x
的所有元素保存到存档(XML 存档)ar
中。
要求 |
|
从存档加载 unordered_flat_map
删除 unordered_flat_map
x
中所有预先存在的元素,并从存档(XML 存档)ar
中插入原始 unordered_flat_map
other
元素的已还原副本(这些元素已保存到 ar
读取的存储区中)。
要求 |
|
将迭代器/const_iterator保存到存档
将iterator
(const_iterator
)it
的位置信息保存到存档(XML存档)ar
中。it
可以是end()
迭代器。
要求 |
|
从存档加载迭代器/const_iterator
使iterator
(const_iterator
)it
指向原始iterator
(const_iterator
)在存档(XML存档)ar
读取的存储中保存的已恢复位置。
要求 |
如果 |
类模板 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_flat_set
/std::unordered_flat_set
的接口。
-
value_type
必须是可移动构造的。 -
在重新哈希时,不会保持指针稳定性。
-
begin()
不是常数时间。 -
没有用于桶处理(
bucket_count
除外)或节点提取/插入的API。 -
容器的最大负载因子由内部管理,用户无法设置。
除此之外,boost::unordered_flat_set
大多可以作为基于节点的标准无序关联容器的直接替代品。
概要
// #include <boost/unordered/unordered_flat_set.hpp> namespace boost { template<class Key, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<Key>> 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<Allocator>::pointer; using const_pointer = typename std::allocator_traits<Allocator>::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 = stats-type; // if statistics are enabled // construct/copy/destroy unordered_flat_set(); explicit unordered_flat_set(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); template<class InputIterator> 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()); unordered_flat_set(const unordered_flat_set& other); unordered_flat_set(unordered_flat_set&& other); template<class InputIterator> unordered_flat_set(InputIterator f, InputIterator l, const allocator_type& a); explicit unordered_flat_set(const Allocator& a); unordered_flat_set(const unordered_flat_set& other, const Allocator& a); unordered_flat_set(concurrent_flat_set<Key, Hash, Pred, Allocator>&& other); unordered_flat_set(std::initializer_list<value_type> il, size_type n = implementation-defined const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); unordered_flat_set(size_type n, const allocator_type& a); unordered_flat_set(size_type n, const hasher& hf, const allocator_type& a); template<class InputIterator> unordered_flat_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a); template<class InputIterator> unordered_flat_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a); unordered_flat_set(std::initializer_list<value_type> il, const allocator_type& a); unordered_flat_set(std::initializer_list<value_type> il, size_type n, const allocator_type& a); unordered_flat_set(std::initializer_list<value_type> il, size_type n, const hasher& hf, const allocator_type& a); ~unordered_flat_set(); unordered_flat_set& operator=(const unordered_flat_set& other); unordered_flat_set& operator=(unordered_flat_set&& other) noexcept( (boost::allocator_traits<Allocator>::is_always_equal::value || boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) && std::is_same<pointer, value_type*>::value); unordered_flat_set& operator=(std::initializer_list<value_type>); allocator_type get_allocator() const noexcept; // iterators iterator begin() noexcept; const_iterator begin() const noexcept; iterator end() noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; // capacity [[nodiscard]] bool empty() const noexcept; size_type size() const noexcept; size_type max_size() const noexcept; // modifiers template<class... Args> std::pair<iterator, bool> emplace(Args&&... args); template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args); std::pair<iterator, bool> insert(const value_type& obj); std::pair<iterator, bool> insert(value_type&& obj); template<class K> std::pair<iterator, bool> insert(K&& k); iterator insert(const_iterator hint, const value_type& obj); iterator insert(const_iterator hint, value_type&& obj); template<class K> iterator insert(const_iterator hint, K&& k); template<class InputIterator> void insert(InputIterator first, InputIterator last); void insert(std::initializer_list<value_type>); convertible-to-iterator erase(iterator position); convertible-to-iterator erase(const_iterator position); size_type erase(const key_type& k); template<class K> size_type erase(K&& k); iterator erase(const_iterator first, const_iterator last); void swap(unordered_flat_set& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || boost::allocator_traits<Allocator>::propagate_on_container_swap::value); void clear() noexcept; template<class H2, class P2> void merge(unordered_flat_set<Key, T, H2, P2, Allocator>& source); template<class H2, class P2> void merge(unordered_flat_set<Key, T, H2, P2, Allocator>&& source); // observers hasher hash_function() const; key_equal key_eq() const; // set operations iterator find(const key_type& k); const_iterator find(const key_type& k) const; template<class K> iterator find(const K& k); template<class K> const_iterator find(const K& k) const; size_type count(const key_type& k) const; template<class K> size_type count(const K& k) const; bool contains(const key_type& k) const; template<class K> bool contains(const K& k) const; std::pair<iterator, iterator> equal_range(const key_type& k); std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const; template<class K> std::pair<iterator, iterator> equal_range(const K& k); template<class K> std::pair<const_iterator, const_iterator> equal_range(const K& k) const; // bucket interface size_type bucket_count() const noexcept; // hash policy float load_factor() const noexcept; float max_load_factor() const noexcept; void max_load_factor(float z); size_type max_load() const noexcept; void rehash(size_type n); void reserve(size_type n); // statistics (if enabled) stats get_stats() const; void reset_stats() noexcept; }; // Deduction Guides template<class InputIterator, class Hash = boost::hash<iter-value-type<InputIterator>>, class Pred = std::equal_to<iter-value-type<InputIterator>>, class Allocator = std::allocator<iter-value-type<InputIterator>>> unordered_flat_set(InputIterator, InputIterator, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_flat_set<iter-value-type<InputIterator>, Hash, Pred, Allocator>; template<class T, class Hash = boost::hash<T>, class Pred = std::equal_to<T>, class Allocator = std::allocator<T>> unordered_flat_set(std::initializer_list<T>, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_flat_set<T, Hash, Pred, Allocator>; template<class InputIterator, class Allocator> unordered_flat_set(InputIterator, InputIterator, typename see below::size_type, Allocator) -> unordered_flat_set<iter-value-type<InputIterator>, boost::hash<iter-value-type<InputIterator>>, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class InputIterator, class Allocator> unordered_flat_set(InputIterator, InputIterator, Allocator) -> unordered_flat_set<iter-value-type<InputIterator>, boost::hash<iter-value-type<InputIterator>>, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class InputIterator, class Hash, class Allocator> unordered_flat_set(InputIterator, InputIterator, typename see below::size_type, Hash, Allocator) -> unordered_flat_set<iter-value-type<InputIterator>, Hash, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class T, class Allocator> unordered_flat_set(std::initializer_list<T>, typename see below::size_type, Allocator) -> unordered_flat_set<T, boost::hash<T>, std::equal_to<T>, Allocator>; template<class T, class Allocator> unordered_flat_set(std::initializer_list<T>, Allocator) -> unordered_flat_set<T, boost::hash<T>, std::equal_to<T>, Allocator>; template<class T, class Hash, class Allocator> unordered_flat_set(std::initializer_list<T>, typename see below::size_type, Hash, Allocator) -> unordered_flat_set<T, Hash, std::equal_to<T>, Allocator>; // Equality Comparisons template<class Key, class T, class Hash, class Pred, class Alloc> bool operator!=(const unordered_flat_set<Key, T, Hash, Pred, Alloc>& x, const unordered_flat_set<Key, T, Hash, Pred, Alloc>& y); template<class Key, class T, class Hash, class Pred, class Alloc> bool operator!=(const unordered_flat_set<Key, T, Hash, Pred, Alloc>& x, const unordered_flat_set<Key, T, Hash, Pred, Alloc>& y); // swap template<class Key, class T, class Hash, class Pred, class Alloc> void swap(unordered_flat_set<Key, T, Hash, Pred, Alloc>& x, unordered_flat_set<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y))); // Erasure template<class K, class T, class H, class P, class A, class Predicate> typename unordered_flat_set<K, T, H, P, A>::size_type erase_if(unordered_flat_set<K, T, H, P, A>& c, Predicate pred); // Pmr aliases (C++17 and up) namespace unordered::pmr { template<class Key, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>> using unordered_flat_set = boost::unordered_flat_set<Key, Hash, Pred, std::pmr::polymorphic_allocator<Key>>; } }
描述
模板参数
键 |
|
哈希 |
一个一元函数对象类型,充当 |
Pred |
一个二元函数对象,它在 |
分配器 |
一个分配器,其值类型与容器的值类型相同。支持使用花哨指针的分配器。 |
容器的元素保存在内部的桶数组中。元素插入到由其哈希码确定的桶中,但如果桶已被占用(冲突),则使用原始位置附近的可用桶。
桶数组的大小可以通过调用insert
/emplace
自动增加,或者通过调用rehash
/reserve
来增加。容器的负载因子(元素数量除以桶的数量)永远不会大于max_load_factor()
,除了在小型尺寸下,实现可能决定允许更高的负载。
如果hash_is_avalanching<Hash>::value
为true
,则哈希函数按原样使用;否则,会添加一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。
类型定义
typedef implementation-defined iterator;
一个常量迭代器,其值类型为 value_type
。
迭代器类别至少是前向迭代器。
可转换为 const_iterator
。
typedef implementation-defined const_iterator;
一个常量迭代器,其值类型为 value_type
。
迭代器类别至少是前向迭代器。
构造函数
默认构造函数
unordered_flat_set();
使用hasher()
作为哈希函数、key_equal()
作为键相等谓词和allocator_type()
作为分配器来构造一个空容器。
后置条件 |
|
要求 |
如果使用默认值,则 |
桶计数构造函数
explicit unordered_flat_set(size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用hf
作为哈希函数、eql
作为键相等谓词和a
作为分配器来构造一个至少包含n
个桶的空容器。
后置条件 |
|
要求 |
如果使用默认值,则 |
迭代器范围构造函数
template<class InputIterator>
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());
使用hf
作为哈希函数、eql
作为键相等谓词和a
作为分配器来构造一个至少包含n
个桶的空容器,并将[f, l)
中的元素插入到其中。
要求 |
如果使用默认值,则 |
复制构造函数
unordered_flat_set(unordered_flat_set const& other);
复制构造函数。复制包含的元素、哈希函数、谓词和分配器。
如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。
要求 |
`value_type` 可复制构造。 |
移动构造函数
unordered_flat_set(unordered_flat_set&& other);
移动构造函数。other
的内部桶数组将直接转移到新容器。哈希函数、谓词和分配器将从 other
移动构造。如果启用了统计信息 启用,则将内部统计信息从 other
传输并调用 other.reset_stats()
。
带分配器的迭代器范围构造函数
template<class InputIterator>
unordered_flat_set(InputIterator f, InputIterator l, const allocator_type& a);
使用a
作为分配器、使用默认的哈希函数和键相等谓词来构造一个空容器,并将[f, l)
中的元素插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带分配器的复制构造函数
unordered_flat_set(unordered_flat_set const& other, Allocator const& a);
构造一个容器,复制other
包含的元素、哈希函数和谓词,但使用分配器a
。
带分配器的移动构造函数
unordered_flat_set(unordered_flat_set&& other, Allocator const& a);
如果 a == other.get_allocator()
,则 other
的元素将直接转移到新容器;否则,将从 other
的元素移动构造元素。哈希函数和谓词将从 other
移动构造,分配器将从 a
复制构造。如果启用了统计信息 启用,则当且仅当 a == other.get_allocator()
时,才会从 other
传输内部统计信息,并且始终调用 other.reset_stats()
。
从 concurrent_flat_set 的移动构造函数
unordered_flat_set(concurrent_flat_set<Key, Hash, Pred, Allocator>&& other);
从 concurrent_flat_set
进行移动构造。other
的内部桶数组将直接转移到新容器。哈希函数、谓词和分配器将从 other
移动构造。如果启用了统计信息 启用,则将内部统计信息从 other
传输并调用 other.reset_stats()
。
复杂度 |
常数时间。 |
并发性 |
阻塞 |
初始化列表构造函数
unordered_flat_set(std::initializer_list<value_type> il,
size_type n = implementation-defined
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用hf
作为哈希函数、eql
作为键相等谓词和a
来构造一个至少包含n
个桶的空容器,并将il
中的元素插入到其中。
要求 |
如果使用默认值,则 |
带分配器的桶计数构造函数
unordered_flat_set(size_type n, allocator_type const& a);
使用hf
作为哈希函数、默认的哈希函数和键相等谓词以及a
作为分配器来构造一个至少包含n
个桶的空容器。
后置条件 |
|
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带哈希函数和分配器的桶计数构造函数
unordered_flat_set(size_type n, hasher const& hf, allocator_type const& a);
使用hf
作为哈希函数、默认的键相等谓词和a
作为分配器来构造一个至少包含n
个桶的空容器。
后置条件 |
|
要求 |
`key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的迭代器范围构造函数
template<class InputIterator>
unordered_flat_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a);
使用a
作为分配器以及默认的哈希函数和键相等谓词来构造一个至少包含n
个桶的空容器,并将[f, l)
中的元素插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和哈希函数的迭代器范围构造函数
template<class InputIterator>
unordered_flat_set(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a);
使用hf
作为哈希函数、a
作为分配器以及默认的键相等谓词来构造一个至少包含n
个桶的空容器,并将[f, l)
中的元素插入到其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
带分配器的初始化列表构造函数
unordered_flat_set(std::initializer_list<value_type> il, const allocator_type& a);
使用a
以及默认的哈希函数和键相等谓词来构造一个空容器,并将il
中的元素插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的初始化列表构造函数
unordered_flat_set(std::initializer_list<value_type> il, size_type n, const allocator_type& a);
使用a
以及默认的哈希函数和键相等谓词来构造一个至少包含n
个桶的空容器,并将il
中的元素插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数、哈希函数和分配器的初始化列表构造函数
unordered_flat_set(std::initializer_list<value_type> il, size_type n, const hasher& hf,
const allocator_type& a);
使用hf
作为哈希函数、a
作为分配器以及默认的键相等谓词来构造一个至少包含n
个桶的空容器,并将il
中的元素插入到其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
赋值
复制赋值
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
元素的副本。
要求 |
|
移动赋值
unordered_flat_set& operator=(unordered_flat_set&& other)
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
std::is_same<pointer, value_type*>::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
传输内部统计信息,并且始终调用 other.reset_stats()
。
迭代器
begin
iterator begin() noexcept;
const_iterator begin() const noexcept;
返回值 |
指向容器第一个元素的迭代器,如果容器为空,则返回容器的尾后值。 |
复杂度 |
O( |
cbegin
const_iterator cbegin() const noexcept;
返回值 |
指向容器第一个元素的 `const_iterator`,如果容器为空,则返回容器的尾后值。 |
复杂度 |
O( |
修改器
emplace
template<class... Args> std::pair<iterator, bool> emplace(Args&&... args);
当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。
要求 |
|
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 |
emplace_hint
template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。
position
是关于应插入元素位置的建议。此实现忽略它。
要求 |
|
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 |
复制插入
std::pair<iterator, bool> insert(const value_type& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
要求 |
`value_type` 可 复制插入。 |
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 |
移动插入
std::pair<iterator, bool> insert(value_type&& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
要求 |
`value_type` 可 移动插入。 |
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 |
透明插入
template<class K> std::pair<iterator, bool> insert(K&& k);
当且仅当容器中没有具有等效键的元素时,才将从std::forward<K>(k)
构造的元素插入到容器中。
要求 |
|
返回值 |
返回类型的布尔分量如果进行了插入则为 true。 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 只有当 |
带提示的复制插入
iterator insert(const_iterator hint, const value_type& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
hint
是关于应插入元素位置的建议。此实现忽略它。
要求 |
`value_type` 可 复制插入。 |
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 |
带提示的移动插入
iterator insert(const_iterator hint, value_type&& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
hint
是关于应插入元素位置的建议。此实现忽略它。
要求 |
`value_type` 可 移动插入。 |
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 |
带提示的透明插入
template<class K> std::pair<iterator, bool> insert(const_iterator hint, K&& k);
当且仅当容器中没有具有等效键的元素时,才将从std::forward<K>(k)
构造的元素插入到容器中。
hint
是关于应插入元素位置的建议。此实现忽略它。
要求 |
|
返回值 |
返回类型的布尔分量如果进行了插入则为 true。 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 只有当 |
插入迭代器范围
template<class InputIterator> void insert(InputIterator first, InputIterator last);
将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。
要求 |
|
抛出 |
插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 |
插入初始化列表
void insert(std::initializer_list<value_type>);
将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。
要求 |
`value_type` 可 复制插入 到容器中。 |
抛出 |
插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。 |
按位置删除
convertible-to-iterator erase(iterator position);
convertible-to-iterator erase(const_iterator position);
删除position
指向的元素。
返回值 |
一个不透明对象,可以隐式转换为擦除之前紧跟在 |
抛出 |
无。 |
注释 |
返回的不透明对象只能被丢弃或立即转换为 |
按键删除
size_type erase(const key_type& k);
template<class K> size_type erase(K&& k);
删除所有键值等效于k
的元素。
返回值 |
删除的元素数量。 |
抛出 |
只有当 |
注释 |
只有当 |
删除范围
iterator erase(const_iterator first, const_iterator last);
删除从first
到last
范围内的元素。
返回值 |
删除元素之后的迭代器 - 即 |
抛出 |
此实现中没有任何操作(既不调用 |
交换 (swap)
void swap(unordered_flat_set& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_swap::value);
将容器的内容与参数交换。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。
抛出 |
除非 |
清空 (clear)
void clear() noexcept;
删除容器中的所有元素。
后置条件 |
|
合并 (merge)
template<class H2, class P2>
void merge(unordered_flat_set<Key, T, H2, P2, Allocator>& source);
template<class H2, class P2>
void merge(unordered_flat_set<Key, T, H2, P2, Allocator>&& source);
将 source
中所有键在 *this
中不存在的元素移动插入到 *this
中,并从 source
中删除这些元素。
查找
查找 (find)
iterator find(const key_type& k);
const_iterator find(const key_type& k) const;
template<class K>
iterator find(const K& k);
返回值 |
指向具有与 |
注释 |
只有当 |
计数 (count)
size_type count(const key_type& k) const;
template<class K>
size_type count(const K& k) const;
返回值 |
键值等效于 |
注释 |
只有当 |
包含 (contains)
bool contains(const key_type& k) const;
template<class K>
bool contains(const K& k) const;
返回值 |
一个布尔值,指示容器中是否存在键等于 |
注释 |
只有当 |
相等范围 (equal_range)
std::pair<iterator, iterator> equal_range(const key_type& k);
std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
template<class K>
std::pair<iterator, iterator> equal_range(const K& k);
template<class K>
std::pair<const_iterator, const_iterator> equal_range(const K& k) const;
返回值 |
包含所有键值等效于 |
注释 |
只有当 |
哈希策略
负载因子 (load_factor)
float load_factor() const noexcept;
返回值 |
|
设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果 |
不执行任何操作,因为不允许用户更改此参数。为了与 |
最大负载
size_type max_load() const noexcept;
返回值 |
容器在不重新哈希的情况下可以容纳的最大元素数量,假设不再删除元素。 |
注意 |
构造、重新哈希或清除后,容器的最大负载至少为 |
重新哈希 (rehash)
void rehash(size_type n);
如有必要,更改桶数组的大小,使其至少包含 n
个桶,并且负载因子小于或等于最大负载因子。在适用情况下,这将扩大或缩小与容器关联的 bucket_count()
。
当 size() == 0
时,rehash(0)
将释放底层桶数组。如果提供的分配器使用高级指针,则随后将执行默认分配。
使迭代器、指针和引用失效,并更改元素的顺序。
抛出 |
除非容器的哈希函数或比较函数抛出异常,否则该函数无效。 |
预留空间 (reserve)
void reserve(size_type n);
等效于 a.rehash(ceil(n / a.max_load_factor()))
。
与rehash
类似,此函数可用于增加或减少容器中桶的数量。
使迭代器、指针和引用失效,并更改元素的顺序。
抛出 |
除非容器的哈希函数或比较函数抛出异常,否则该函数无效。 |
推导指南
如果出现以下任何情况,则推导指南不会参与重载解析:
-
它具有
InputIterator
模板参数,并且为该参数推导的类型不符合输入迭代器的资格。 -
它具有
Allocator
模板参数,并且为该参数推导的类型不符合分配器的资格。 -
它具有
Hash
模板参数,并且为该参数推导的类型为整数类型或符合分配器资格的类型。 -
它具有
Pred
模板参数,并且为该参数推导的类型符合分配器资格的类型。
推导指南中的size_type
参数类型引用由推导指南推导出的容器类型的size_type
成员类型。其默认值与所选构造函数的默认值一致。
迭代器值类型 (iter-value-type)
template<class InputIterator> using iter-value-type = typename std::iterator_traits<InputIterator>::value_type; // exposition only
相等比较
相等运算符 (operator==)
template<class Key, class T, class Hash, class Pred, class Alloc>
bool operator==(const unordered_flat_set<Key, T, Hash, Pred, Alloc>& x,
const unordered_flat_set<Key, T, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个具有相同键和相同值(使用operator==
比较值类型)的元素,则返回true
。
注释 |
如果两个容器没有等效的相等谓词,则行为未定义。 |
不相等运算符 (operator!=)
template<class Key, class T, class Hash, class Pred, class Alloc>
bool operator!=(const unordered_flat_set<Key, T, Hash, Pred, Alloc>& x,
const unordered_flat_set<Key, T, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个具有相同键和相同值(使用operator==
比较值类型)的元素,则返回false
。
注释 |
如果两个容器没有等效的相等谓词,则行为未定义。 |
交换
template<class Key, class T, class Hash, class Pred, class Alloc>
void swap(unordered_flat_set<Key, T, Hash, Pred, Alloc>& x,
unordered_flat_set<Key, T, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
交换x
和y
的内容。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。
效果 |
|
抛出 |
除非 |
erase_if
template<class K, class T, class H, class P, class A, class Predicate>
typename unordered_flat_set<K, T, H, P, A>::size_type
erase_if(unordered_flat_set<K, T, H, P, A>& c, Predicate pred);
遍历容器c
,并移除所有满足提供的谓词返回true
的元素。
返回值 |
被移除元素的数量。 |
注释 |
等价于
|
序列化
可以使用此库提供的 API 通过 Boost.Serialization 存档/检索 unordered_flat_set
。支持常规存档和 XML 存档。
将 unordered_flat_set 保存到存档
将 unordered_flat_set
x
的所有元素保存到存档(XML 存档)ar
中。
要求 |
|
从存档加载 unordered_flat_set
删除 unordered_flat_set
x
中所有预先存在的元素,并从存档(XML 存档)ar
中插入原始 unordered_flat_set
other
元素的已还原副本(这些元素已保存到 ar
读取的存储区中)。
要求 |
|
将迭代器/const_iterator保存到存档
将iterator
(const_iterator
)it
的位置信息保存到存档(XML存档)ar
中。it
可以是end()
迭代器。
要求 |
|
从存档加载迭代器/const_iterator
使iterator
(const_iterator
)it
指向原始iterator
(const_iterator
)在存档(XML存档)ar
读取的存储中保存的已恢复位置。
要求 |
如果 |
类模板 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
大多可以作为标准无序关联容器的直接替代品。
概要
// #include <boost/unordered/unordered_node_map.hpp> namespace boost { template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T>>> class unordered_node_map { public: // types using key_type = Key; using mapped_type = T; using value_type = std::pair<const Key, T>; using init_type = std::pair< typename std::remove_const<Key>::type, typename std::remove_const<T>::type >; using hasher = Hash; using key_equal = Pred; using allocator_type = Allocator; using pointer = typename std::allocator_traits<Allocator>::pointer; using const_pointer = typename std::allocator_traits<Allocator>::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 = stats-type; // if statistics are enabled // construct/copy/destroy unordered_node_map(); explicit unordered_node_map(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); template<class InputIterator> 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()); unordered_node_map(const unordered_node_map& other); unordered_node_map(unordered_node_map&& other); template<class InputIterator> unordered_node_map(InputIterator f, InputIterator l, const allocator_type& a); explicit unordered_node_map(const Allocator& a); unordered_node_map(const unordered_node_map& other, const Allocator& a); unordered_node_map(unordered_node_map&& other, const Allocator& a); unordered_node_map(concurrent_node_map<Key, T, Hash, Pred, Allocator>&& other); unordered_node_map(std::initializer_list<value_type> il, size_type n = implementation-defined const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); unordered_node_map(size_type n, const allocator_type& a); unordered_node_map(size_type n, const hasher& hf, const allocator_type& a); template<class InputIterator> unordered_node_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a); template<class InputIterator> unordered_node_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a); unordered_node_map(std::initializer_list<value_type> il, const allocator_type& a); unordered_node_map(std::initializer_list<value_type> il, size_type n, const allocator_type& a); unordered_node_map(std::initializer_list<value_type> il, size_type n, const hasher& hf, const allocator_type& a); ~unordered_node_map(); unordered_node_map& operator=(const unordered_node_map& other); unordered_node_map& operator=(unordered_node_map&& other) noexcept( (boost::allocator_traits<Allocator>::is_always_equal::value || boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) && std::is_same<pointer, value_type*>::value); unordered_node_map& operator=(std::initializer_list<value_type>); allocator_type get_allocator() const noexcept; // iterators iterator begin() noexcept; const_iterator begin() const noexcept; iterator end() noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; // capacity [[nodiscard]] bool empty() const noexcept; size_type size() const noexcept; size_type max_size() const noexcept; // modifiers template<class... Args> std::pair<iterator, bool> emplace(Args&&... args); template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args); std::pair<iterator, bool> insert(const value_type& obj); std::pair<iterator, bool> insert(const init_type& obj); std::pair<iterator, bool> insert(value_type&& obj); std::pair<iterator, bool> insert(init_type&& obj); iterator insert(const_iterator hint, const value_type& obj); iterator insert(const_iterator hint, const init_type& obj); iterator insert(const_iterator hint, value_type&& obj); iterator insert(const_iterator hint, init_type&& obj); template<class InputIterator> void insert(InputIterator first, InputIterator last); void insert(std::initializer_list<value_type>); insert_return_type insert(node_type&& nh); iterator insert(const_iterator hint, node_type&& nh); template<class... Args> std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args); template<class... Args> std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args); template<class K, class... Args> std::pair<iterator, bool> try_emplace(K&& k, Args&&... args); template<class... Args> iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); template<class... Args> iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); template<class K, class... Args> iterator try_emplace(const_iterator hint, K&& k, Args&&... args); template<class M> std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj); template<class M> std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj); template<class K, class M> std::pair<iterator, bool> insert_or_assign(K&& k, M&& obj); template<class M> iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); template<class M> iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); template<class K, class M> iterator insert_or_assign(const_iterator hint, K&& k, M&& obj); convertible-to-iterator erase(iterator position); convertible-to-iterator erase(const_iterator position); size_type erase(const key_type& k); template<class K> size_type erase(K&& k); iterator erase(const_iterator first, const_iterator last); void swap(unordered_node_map& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || boost::allocator_traits<Allocator>::propagate_on_container_swap::value); node_type extract(const_iterator position); node_type extract(const key_type& key); template<class K> node_type extract(K&& key); void clear() noexcept; template<class H2, class P2> void merge(unordered_node_map<Key, T, H2, P2, Allocator>& source); template<class H2, class P2> void merge(unordered_node_map<Key, T, H2, P2, Allocator>&& source); // observers hasher hash_function() const; key_equal key_eq() const; // map operations iterator find(const key_type& k); const_iterator find(const key_type& k) const; template<class K> iterator find(const K& k); template<class K> const_iterator find(const K& k) const; size_type count(const key_type& k) const; template<class K> size_type count(const K& k) const; bool contains(const key_type& k) const; template<class K> bool contains(const K& k) const; std::pair<iterator, iterator> equal_range(const key_type& k); std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const; template<class K> std::pair<iterator, iterator> equal_range(const K& k); template<class K> std::pair<const_iterator, const_iterator> equal_range(const K& k) const; // element access mapped_type& operator[](const key_type& k); mapped_type& operator[](key_type&& k); template<class K> mapped_type& operator[](K&& k); mapped_type& at(const key_type& k); const mapped_type& at(const key_type& k) const; template<class K> mapped_type& at(const K& k); template<class K> const mapped_type& at(const K& k) const; // bucket interface size_type bucket_count() const noexcept; // hash policy float load_factor() const noexcept; float max_load_factor() const noexcept; void max_load_factor(float z); size_type max_load() const noexcept; void rehash(size_type n); void reserve(size_type n); // statistics (if enabled) stats get_stats() const; void reset_stats() noexcept; }; // Deduction Guides template<class InputIterator, class Hash = boost::hash<iter-key-type<InputIterator>>, class Pred = std::equal_to<iter-key-type<InputIterator>>, class Allocator = std::allocator<iter-to-alloc-type<InputIterator>>> unordered_node_map(InputIterator, InputIterator, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_node_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, Hash, Pred, Allocator>; template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T>>> unordered_node_map(std::initializer_list<std::pair<Key, T>>, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_node_map<Key, T, Hash, Pred, Allocator>; template<class InputIterator, class Allocator> unordered_node_map(InputIterator, InputIterator, typename see below::size_type, Allocator) -> unordered_node_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, boost::hash<iter-key-type<InputIterator>>, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class InputIterator, class Allocator> unordered_node_map(InputIterator, InputIterator, Allocator) -> unordered_node_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, boost::hash<iter-key-type<InputIterator>>, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class InputIterator, class Hash, class Allocator> unordered_node_map(InputIterator, InputIterator, typename see below::size_type, Hash, Allocator) -> unordered_node_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, Hash, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class Key, class T, class Allocator> unordered_node_map(std::initializer_list<std::pair<Key, T>>, typename see below::size_type, Allocator) -> unordered_node_map<Key, T, boost::hash<Key>, std::equal_to<Key>, Allocator>; template<class Key, class T, class Allocator> unordered_node_map(std::initializer_list<std::pair<Key, T>>, Allocator) -> unordered_node_map<Key, T, boost::hash<Key>, std::equal_to<Key>, Allocator>; template<class Key, class T, class Hash, class Allocator> unordered_node_map(std::initializer_list<std::pair<Key, T>>, typename see below::size_type, Hash, Allocator) -> unordered_node_map<Key, T, Hash, std::equal_to<Key>, Allocator>; // Equality Comparisons template<class Key, class T, class Hash, class Pred, class Alloc> bool operator==(const unordered_node_map<Key, T, Hash, Pred, Alloc>& x, const unordered_node_map<Key, T, Hash, Pred, Alloc>& y); template<class Key, class T, class Hash, class Pred, class Alloc> bool operator!=(const unordered_node_map<Key, T, Hash, Pred, Alloc>& x, const unordered_node_map<Key, T, Hash, Pred, Alloc>& y); // swap template<class Key, class T, class Hash, class Pred, class Alloc> void swap(unordered_node_map<Key, T, Hash, Pred, Alloc>& x, unordered_node_map<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y))); // Erasure template<class K, class T, class H, class P, class A, class Predicate> typename unordered_node_map<K, T, H, P, A>::size_type erase_if(unordered_node_map<K, T, H, P, A>& c, Predicate pred); // Pmr aliases (C++17 and up) namespace unordered::pmr { template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>> using unordered_node_map = boost::unordered_node_map<Key, T, Hash, Pred, std::pmr::polymorphic_allocator<std::pair<const Key, T>>>; } }
描述
模板参数
键 |
|
T |
|
哈希 |
一个一元函数对象类型,充当 |
Pred |
一个二元函数对象,它在 |
分配器 |
一个分配器,其值类型与容器的值类型相同。支持使用花哨指针的分配器。 |
容器的元素节点存储在一个内部的桶数组中。节点插入到由其元素的哈希码确定的桶中,但是如果桶已被占用(发生冲突),则使用原始位置附近的可用桶。
桶数组的大小可以通过调用insert
/emplace
自动增加,或者通过调用rehash
/reserve
来增加。容器的负载因子(元素数量除以桶的数量)永远不会大于max_load_factor()
,除了在小型尺寸下,实现可能决定允许更高的负载。
如果hash_is_avalanching<Hash>::value
为true
,则哈希函数按原样使用;否则,会添加一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。
类型定义
typedef implementation-defined iterator;
一个迭代器,其值类型为 value_type
。
迭代器类别至少是前向迭代器。
可转换为 const_iterator
。
typedef implementation-defined const_iterator;
一个常量迭代器,其值类型为 value_type
。
迭代器类别至少是前向迭代器。
typedef implementation-defined node_type;
一个用于保存已提取容器元素的类,模拟NodeHandle。
typedef implementation-defined insert_return_type;
内部类模板的特化
template<class Iterator, class NodeType>
struct insert_return_type // name is exposition only
{
Iterator position;
bool inserted;
NodeType node;
};
其中 Iterator
= iterator
和 NodeType
= node_type
。
构造函数
默认构造函数
unordered_node_map();
使用hasher()
作为哈希函数、key_equal()
作为键相等谓词和allocator_type()
作为分配器来构造一个空容器。
后置条件 |
|
要求 |
如果使用默认值,则 |
桶计数构造函数
explicit unordered_node_map(size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用hf
作为哈希函数、eql
作为键相等谓词和a
作为分配器来构造一个至少包含n
个桶的空容器。
后置条件 |
|
要求 |
如果使用默认值,则 |
迭代器范围构造函数
template<class InputIterator>
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());
使用hf
作为哈希函数、eql
作为键相等谓词和a
作为分配器来构造一个至少包含n
个桶的空容器,并将[f, l)
中的元素插入到其中。
要求 |
如果使用默认值,则 |
复制构造函数
unordered_node_map(unordered_node_map const& other);
复制构造函数。复制包含的元素、哈希函数、谓词和分配器。
如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。
移动构造函数
unordered_node_map(unordered_node_map&& other);
移动构造函数。other
的内部桶数组直接转移到新的容器。哈希函数、谓词和分配器从 other
移动构造。如果启用了统计信息 enabled,则将内部统计信息从 other
传输,并调用 other.reset_stats()
。
带分配器的迭代器范围构造函数
template<class InputIterator>
unordered_node_map(InputIterator f, InputIterator l, const allocator_type& a);
使用a
作为分配器、使用默认的哈希函数和键相等谓词来构造一个空容器,并将[f, l)
中的元素插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带分配器的复制构造函数
unordered_node_map(unordered_node_map const& other, Allocator const& a);
构造一个容器,复制other
包含的元素、哈希函数和谓词,但使用分配器a
。
带分配器的移动构造函数
unordered_node_map(unordered_node_map&& other, Allocator const& a);
如果 a == other.get_allocator()
,则 other
的元素节点直接转移到新的容器;否则,元素将从 other
的元素移动构造。哈希函数和谓词从 other
移动构造,分配器从 a
复制构造。如果启用了统计信息 enabled,则当且仅当 a == other.get_allocator()
时,才将内部统计信息从 other
传输,并且始终调用 other.reset_stats()
。
来自 concurrent_node_map 的移动构造函数
unordered_node_map(concurrent_node_map<Key, T, Hash, Pred, Allocator>&& other);
从 concurrent_node_map
移动构造。other
的内部桶数组直接转移到新的容器。哈希函数、谓词和分配器从 other
移动构造。如果启用了统计信息 enabled,则将内部统计信息从 other
传输,并调用 other.reset_stats()
。
复杂度 |
常数时间。 |
并发性 |
阻塞 |
初始化列表构造函数
unordered_node_map(std::initializer_list<value_type> il,
size_type n = implementation-defined
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用hf
作为哈希函数、eql
作为键相等谓词和a
来构造一个至少包含n
个桶的空容器,并将il
中的元素插入到其中。
要求 |
如果使用默认值,则 |
带分配器的桶计数构造函数
unordered_node_map(size_type n, allocator_type const& a);
使用hf
作为哈希函数、默认的哈希函数和键相等谓词以及a
作为分配器来构造一个至少包含n
个桶的空容器。
后置条件 |
|
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带哈希函数和分配器的桶计数构造函数
unordered_node_map(size_type n, hasher const& hf, allocator_type const& a);
使用hf
作为哈希函数、默认的键相等谓词和a
作为分配器来构造一个至少包含n
个桶的空容器。
后置条件 |
|
要求 |
`key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的迭代器范围构造函数
template<class InputIterator>
unordered_node_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a);
使用a
作为分配器以及默认的哈希函数和键相等谓词来构造一个至少包含n
个桶的空容器,并将[f, l)
中的元素插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和哈希函数的迭代器范围构造函数
template<class InputIterator>
unordered_node_map(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a);
使用hf
作为哈希函数、a
作为分配器以及默认的键相等谓词来构造一个至少包含n
个桶的空容器,并将[f, l)
中的元素插入到其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
带分配器的初始化列表构造函数
unordered_node_map(std::initializer_list<value_type> il, const allocator_type& a);
使用a
以及默认的哈希函数和键相等谓词来构造一个空容器,并将il
中的元素插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的初始化列表构造函数
unordered_node_map(std::initializer_list<value_type> il, size_type n, const allocator_type& a);
使用a
以及默认的哈希函数和键相等谓词来构造一个至少包含n
个桶的空容器,并将il
中的元素插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数、哈希函数和分配器的初始化列表构造函数
unordered_node_map(std::initializer_list<value_type> il, size_type n, const hasher& hf,
const allocator_type& a);
使用hf
作为哈希函数、a
作为分配器以及默认的键相等谓词来构造一个至少包含n
个桶的空容器,并将il
中的元素插入到其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
赋值
复制赋值
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
元素的副本。
要求 |
|
移动赋值
unordered_node_map& operator=(unordered_node_map&& other)
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
std::is_same<pointer, value_type*>::value);
移动赋值运算符。销毁先前存在的元素,交换 other
的哈希函数和谓词,如果 Alloc::propagate_on_container_move_assignment
存在并且 Alloc::propagate_on_container_move_assignment::value
为 true
,则从 other
移动赋值分配器。如果此时分配器等于 other.get_allocator()
,则 other
的内部桶数组直接转移到新的容器;否则,插入 other
元素的移动构造的副本。如果启用了统计信息 enabled,则当且仅当最终分配器等于 other.get_allocator()
时,才将内部统计信息从 other
传输,并且始终调用 other.reset_stats()
。
迭代器
begin
iterator begin() noexcept;
const_iterator begin() const noexcept;
返回值 |
指向容器第一个元素的迭代器,如果容器为空,则返回容器的尾后值。 |
复杂度 |
O( |
cbegin
const_iterator cbegin() const noexcept;
返回值 |
指向容器第一个元素的 `const_iterator`,如果容器为空,则返回容器的尾后值。 |
复杂度 |
O( |
修改器
emplace
template<class... Args> std::pair<iterator, bool> emplace(Args&&... args);
当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。
要求 |
|
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 如果 |
emplace_hint
template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。
position
是关于应插入元素位置的建议。此实现忽略它。
要求 |
|
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 如果 |
复制插入
std::pair<iterator, bool> insert(const value_type& obj);
std::pair<iterator, bool> insert(const init_type& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
要求 |
`value_type` 可 复制插入。 |
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 当 |
移动插入
std::pair<iterator, bool> insert(value_type&& obj);
std::pair<iterator, bool> insert(init_type&& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
要求 |
`value_type` 可 移动插入。 |
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 当 |
带提示的复制插入
iterator insert(const_iterator hint, const value_type& obj);
iterator insert(const_iterator hint, const init_type& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
hint
是关于应插入元素位置的建议。此实现忽略它。
要求 |
`value_type` 可 复制插入。 |
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 当 |
带提示的移动插入
iterator insert(const_iterator hint, value_type&& obj);
iterator insert(const_iterator hint, init_type&& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
hint
是关于应插入元素位置的建议。此实现忽略它。
要求 |
`value_type` 可 移动插入。 |
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 当 |
插入迭代器范围
template<class InputIterator> void insert(InputIterator first, InputIterator last);
将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。
要求 |
|
抛出 |
插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 |
插入初始化列表
void insert(std::initializer_list<value_type>);
将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。
要求 |
`value_type` 可 复制插入 到容器中。 |
抛出 |
插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 |
插入节点
insert_return_type insert(node_type&& nh);
如果 nh
不为空,当且仅当容器中没有键与 nh.key()
等效的元素时,才将关联的元素插入到容器中。函数返回时,nh
为空。
返回值 |
从
|
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
如果 |
使用提示插入节点
iterator insert(const_iterator hint, node_type&& nh);
如果 nh
不为空,当且仅当容器中没有键与 nh.key()
等效的元素时,才将关联的元素插入到容器中。如果进行了插入,则 nh
变成空,否则不变。
hint
是关于应插入元素位置的建议。此实现忽略它。
返回值 |
如果 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
如果 |
try_emplace
template<class... Args>
std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args);
template<class... Args>
std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args);
template<class K, class... Args>
std::pair<iterator, bool> try_emplace(K&& k, Args&&... args);
如果容器中不存在具有键 `k` 的元素,则将新元素插入容器中。
如果存在具有键 `k` 的元素,则此函数不执行任何操作。
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
此函数类似于 emplace,不同之处在于,如果存在具有等效键的元素,则不构造
不同于 emplace,它只是将所有参数转发到 可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 `template<class K, class... Args>` 重载只有在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义,并且 `iterator` 和 `const_iterator` 都不可以隐式转换为 `K` 时才参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这使得异构查找能够避免实例化 `Key` 类型实例的成本。 |
带提示的 try_emplace
template<class... Args>
iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args);
template<class... Args>
iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args);
template<class K, class... Args>
iterator try_emplace(const_iterator hint, K&& k, Args&&... args);
如果容器中不存在具有键 `k` 的元素,则将新元素插入容器中。
如果存在具有键 `k` 的元素,则此函数不执行任何操作。
hint
是关于应插入元素位置的建议。此实现忽略它。
返回值 |
如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
此函数类似于 emplace_hint,不同之处在于,如果存在具有等效键的元素,则不构造
不同于 emplace_hint,它只是将所有参数转发到 可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 `template<class K, class... Args>` 重载只有在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义,并且 `iterator` 和 `const_iterator` 都不可以隐式转换为 `K` 时才参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这使得异构查找能够避免实例化 `Key` 类型实例的成本。 |
insert_or_assign
template<class M>
std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj);
template<class M>
std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj);
template<class K, class M>
std::pair<iterator, bool> insert_or_assign(K&& k, M&& obj);
将新元素插入容器中,或通过赋值给包含的值来更新现有元素。
如果存在具有键 `k` 的元素,则通过赋值 `std::forward<M>(obj)` 来更新它。
如果没有这样的元素,则将其添加到容器中,如下所示:
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 `template<class K, class M>` 只有在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义时才参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这使得异构查找能够避免实例化 `Key` 类型实例的成本。 |
带提示的 insert_or_assign
template<class M>
iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj);
template<class M>
iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj);
template<class K, class M>
iterator insert_or_assign(const_iterator hint, K&& k, M&& obj);
将新元素插入容器中,或通过赋值给包含的值来更新现有元素。
如果存在具有键 `k` 的元素,则通过赋值 `std::forward<M>(obj)` 来更新它。
如果没有这样的元素,则将其添加到容器中,如下所示:
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
hint
是关于应插入元素位置的建议。此实现忽略它。
返回值 |
如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 `template<class K, class M>` 只有在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义时才参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这使得异构查找能够避免实例化 `Key` 类型实例的成本。 |
按位置删除
convertible-to-iterator erase(iterator position);
convertible-to-iterator erase(const_iterator position);
删除position
指向的元素。
返回值 |
一个不透明对象,可以隐式转换为擦除之前紧跟在 |
抛出 |
无。 |
注释 |
返回的不透明对象只能被丢弃或立即转换为 |
按键删除
size_type erase(const key_type& k);
template<class K> size_type erase(K&& k);
删除所有键值等效于k
的元素。
返回值 |
删除的元素数量。 |
抛出 |
只有当 |
注释 |
只有当 |
删除范围
iterator erase(const_iterator first, const_iterator last);
删除从first
到last
范围内的元素。
返回值 |
删除元素之后的迭代器 - 即 |
抛出 |
此实现中没有任何操作(既不调用 |
交换 (swap)
void swap(unordered_node_map& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_swap::value);
将容器的内容与参数交换。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。
抛出 |
除非 |
按位置提取
node_type extract(const_iterator position);
提取 position
指向的元素。
返回值 |
包含提取元素的 |
抛出 |
无。 |
通过键提取
node_type extract(const key_type& k);
template<class K> node_type extract(K&& k);
提取键与 k
等效的元素(如果存在)。
返回值 |
包含提取元素的 |
抛出 |
只有当 |
注释 |
只有当 |
清空 (clear)
void clear() noexcept;
删除容器中的所有元素。
后置条件 |
|
合并 (merge)
template<class H2, class P2>
void merge(unordered_node_map<Key, T, H2, P2, Allocator>& source);
template<class H2, class P2>
void merge(unordered_node_map<Key, T, H2, P2, Allocator>&& source);
从 source
传输所有键在 *this
中不存在的元素节点。
要求 |
|
注释 |
使指向已传输元素的迭代器失效。如果 |
查找
查找 (find)
iterator find(const key_type& k);
const_iterator find(const key_type& k) const;
template<class K>
iterator find(const K& k);
返回值 |
指向具有与 |
注释 |
只有当 |
计数 (count)
size_type count(const key_type& k) const;
template<class K>
size_type count(const K& k) const;
返回值 |
键值等效于 |
注释 |
只有当 |
包含 (contains)
bool contains(const key_type& k) const;
template<class K>
bool contains(const K& k) const;
返回值 |
一个布尔值,指示容器中是否存在键等于 |
注释 |
只有当 |
相等范围 (equal_range)
std::pair<iterator, iterator> equal_range(const key_type& k);
std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
template<class K>
std::pair<iterator, iterator> equal_range(const K& k);
template<class K>
std::pair<const_iterator, const_iterator> equal_range(const K& k) const;
返回值 |
包含所有键值等效于 |
注释 |
只有当 |
下标运算符 (operator[])
mapped_type& operator[](const key_type& k);
mapped_type& operator[](key_type&& k);
template<class K> mapped_type& operator[](K&& k);
效果 |
如果容器还不包含具有与 |
返回值 |
|
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 只有当 |
at方法
mapped_type& at(const key_type& k);
const mapped_type& at(const key_type& k) const;
template<class K> mapped_type& at(const K& k);
template<class K> const mapped_type& at(const K& k) const;
返回值 |
|
抛出 |
如果不存在这样的元素,则抛出类型为 |
注释 |
只有当 |
哈希策略
负载因子 (load_factor)
float load_factor() const noexcept;
返回值 |
|
设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果 |
不执行任何操作,因为不允许用户更改此参数。为了与 |
最大负载
size_type max_load() const noexcept;
返回值 |
容器在不重新哈希的情况下可以容纳的最大元素数量,假设不再删除元素。 |
注意 |
构造、重新哈希或清除后,容器的最大负载至少为 |
重新哈希 (rehash)
void rehash(size_type n);
如有必要,更改桶数组的大小,使其至少包含 n
个桶,并且负载因子小于或等于最大负载因子。在适用情况下,这将扩大或缩小与容器关联的 bucket_count()
。
当 size() == 0
时,rehash(0)
将释放底层桶数组。如果提供的分配器使用高级指针,则随后将执行默认分配。
使迭代器失效并更改元素的顺序。
抛出 |
除非容器的哈希函数或比较函数抛出异常,否则该函数无效。 |
预留空间 (reserve)
void reserve(size_type n);
等效于 a.rehash(ceil(n / a.max_load_factor()))
。
与rehash
类似,此函数可用于增加或减少容器中桶的数量。
使迭代器失效并更改元素的顺序。
抛出 |
除非容器的哈希函数或比较函数抛出异常,否则该函数无效。 |
推导指南
如果出现以下任何情况,则推导指南不会参与重载解析:
-
它具有
InputIterator
模板参数,并且为该参数推导的类型不符合输入迭代器的资格。 -
它具有
Allocator
模板参数,并且为该参数推导的类型不符合分配器的资格。 -
它具有
Hash
模板参数,并且为该参数推导的类型为整数类型或符合分配器资格的类型。 -
它具有
Pred
模板参数,并且为该参数推导的类型符合分配器资格的类型。
推导指南中的size_type
参数类型引用由推导指南推导出的容器类型的size_type
成员类型。其默认值与所选构造函数的默认值一致。
迭代器值类型 (iter-value-type)
template<class InputIterator> using iter-value-type = typename std::iterator_traits<InputIterator>::value_type; // exposition only
迭代器键类型 (iter-key-type)
template<class InputIterator> using iter-key-type = std::remove_const_t< std::tuple_element_t<0, iter-value-type<InputIterator>>>; // exposition only
迭代器映射类型 (iter-mapped-type)
template<class InputIterator> using iter-mapped-type = std::tuple_element_t<1, iter-value-type<InputIterator>>; // exposition only
迭代器到分配器类型 (iter-to-alloc-type)
template<class InputIterator> using iter-to-alloc-type = std::pair< std::add_const_t<std::tuple_element_t<0, iter-value-type<InputIterator>>>, std::tuple_element_t<1, iter-value-type<InputIterator>>>; // exposition only
相等比较
相等运算符 (operator==)
template<class Key, class T, class Hash, class Pred, class Alloc>
bool operator==(const unordered_node_map<Key, T, Hash, Pred, Alloc>& x,
const unordered_node_map<Key, T, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个具有相同键和相同值(使用operator==
比较值类型)的元素,则返回true
。
注释 |
如果两个容器没有等效的相等谓词,则行为未定义。 |
不相等运算符 (operator!=)
template<class Key, class T, class Hash, class Pred, class Alloc>
bool operator!=(const unordered_node_map<Key, T, Hash, Pred, Alloc>& x,
const unordered_node_map<Key, T, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个具有相同键和相同值(使用operator==
比较值类型)的元素,则返回false
。
注释 |
如果两个容器没有等效的相等谓词,则行为未定义。 |
交换
template<class Key, class T, class Hash, class Pred, class Alloc>
void swap(unordered_node_map<Key, T, Hash, Pred, Alloc>& x,
unordered_node_map<Key, T, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
交换x
和y
的内容。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。
效果 |
|
抛出 |
除非 |
erase_if
template<class K, class T, class H, class P, class A, class Predicate>
typename unordered_node_map<K, T, H, P, A>::size_type
erase_if(unordered_node_map<K, T, H, P, A>& c, Predicate pred);
遍历容器c
,并移除所有满足提供的谓词返回true
的元素。
返回值 |
被移除元素的数量。 |
注释 |
等价于
|
序列化
可以使用此库提供的 API,通过 Boost.Serialization 存档/检索 unordered_node_map
。支持常规存档和 XML 存档。
将 unordered_node_map 保存到存档
将 unordered_node_map
x
的所有元素保存到存档(XML 存档)ar
中。
要求 |
|
从存档加载 unordered_node_map
删除 unordered_node_map
x
中所有预先存在的元素,并从存档(XML 存档)ar
中插入原始 unordered_node_map
other
的元素的已恢复副本(这些元素已保存到 ar
读取的存储区中)。
要求 |
|
将迭代器/const_iterator保存到存档
将iterator
(const_iterator
)it
的位置信息保存到存档(XML存档)ar
中。it
可以是end()
迭代器。
要求 |
|
从存档加载迭代器/const_iterator
使iterator
(const_iterator
)it
指向原始iterator
(const_iterator
)在存档(XML存档)ar
读取的存储中保存的已恢复位置。
要求 |
如果 |
类模板 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
大部分情况下可以直接替换标准的无序关联容器。
概要
// #include <boost/unordered/unordered_node_set.hpp> namespace boost { template<class Key, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<Key>> class unordered_node_set { public: // types using key_type = Key; using value_type = Key; using init_type = Key; using hasher = Hash; using key_equal = Pred; using allocator_type = Allocator; using pointer = typename std::allocator_traits<Allocator>::pointer; using const_pointer = typename std::allocator_traits<Allocator>::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 = stats-type; // if statistics are enabled // construct/copy/destroy unordered_node_set(); explicit unordered_node_set(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); template<class InputIterator> 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()); unordered_node_set(const unordered_node_set& other); unordered_node_set(unordered_node_set&& other); template<class InputIterator> unordered_node_set(InputIterator f, InputIterator l, const allocator_type& a); explicit unordered_node_set(const Allocator& a); unordered_node_set(const unordered_node_set& other, const Allocator& a); unordered_node_set(unordered_node_set&& other, const Allocator& a); unordered_node_set(concurrent_node_set<Key, Hash, Pred, Allocator>&& other); unordered_node_set(std::initializer_list<value_type> il, size_type n = implementation-defined const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); unordered_node_set(size_type n, const allocator_type& a); unordered_node_set(size_type n, const hasher& hf, const allocator_type& a); template<class InputIterator> unordered_node_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a); template<class InputIterator> unordered_node_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a); unordered_node_set(std::initializer_list<value_type> il, const allocator_type& a); unordered_node_set(std::initializer_list<value_type> il, size_type n, const allocator_type& a); unordered_node_set(std::initializer_list<value_type> il, size_type n, const hasher& hf, const allocator_type& a); ~unordered_node_set(); unordered_node_set& operator=(const unordered_node_set& other); unordered_node_set& operator=(unordered_node_set&& other) noexcept( (boost::allocator_traits<Allocator>::is_always_equal::value || boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) && std::is_same<pointer, value_type*>::value); unordered_node_set& operator=(std::initializer_list<value_type>); allocator_type get_allocator() const noexcept; // iterators iterator begin() noexcept; const_iterator begin() const noexcept; iterator end() noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; // capacity [[nodiscard]] bool empty() const noexcept; size_type size() const noexcept; size_type max_size() const noexcept; // modifiers template<class... Args> std::pair<iterator, bool> emplace(Args&&... args); template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args); std::pair<iterator, bool> insert(const value_type& obj); std::pair<iterator, bool> insert(value_type&& obj); template<class K> std::pair<iterator, bool> insert(K&& k); iterator insert(const_iterator hint, const value_type& obj); iterator insert(const_iterator hint, value_type&& obj); template<class K> iterator insert(const_iterator hint, K&& k); template<class InputIterator> void insert(InputIterator first, InputIterator last); void insert(std::initializer_list<value_type>); insert_return_type insert(node_type&& nh); iterator insert(const_iterator hint, node_type&& nh); convertible-to-iterator erase(iterator position); convertible-to-iterator erase(const_iterator position); size_type erase(const key_type& k); template<class K> size_type erase(K&& k); iterator erase(const_iterator first, const_iterator last); void swap(unordered_node_set& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || boost::allocator_traits<Allocator>::propagate_on_container_swap::value); node_type extract(const_iterator position); node_type extract(const key_type& key); template<class K> node_type extract(K&& key); void clear() noexcept; template<class H2, class P2> void merge(unordered_node_set<Key, T, H2, P2, Allocator>& source); template<class H2, class P2> void merge(unordered_node_set<Key, T, H2, P2, Allocator>&& source); // observers hasher hash_function() const; key_equal key_eq() const; // set operations iterator find(const key_type& k); const_iterator find(const key_type& k) const; template<class K> iterator find(const K& k); template<class K> const_iterator find(const K& k) const; size_type count(const key_type& k) const; template<class K> size_type count(const K& k) const; bool contains(const key_type& k) const; template<class K> bool contains(const K& k) const; std::pair<iterator, iterator> equal_range(const key_type& k); std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const; template<class K> std::pair<iterator, iterator> equal_range(const K& k); template<class K> std::pair<const_iterator, const_iterator> equal_range(const K& k) const; // bucket interface size_type bucket_count() const noexcept; // hash policy float load_factor() const noexcept; float max_load_factor() const noexcept; void max_load_factor(float z); size_type max_load() const noexcept; void rehash(size_type n); void reserve(size_type n); // statistics (if enabled) stats get_stats() const; void reset_stats() noexcept; }; // Deduction Guides template<class InputIterator, class Hash = boost::hash<iter-value-type<InputIterator>>, class Pred = std::equal_to<iter-value-type<InputIterator>>, class Allocator = std::allocator<iter-value-type<InputIterator>>> unordered_node_set(InputIterator, InputIterator, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_node_set<iter-value-type<InputIterator>, Hash, Pred, Allocator>; template<class T, class Hash = boost::hash<T>, class Pred = std::equal_to<T>, class Allocator = std::allocator<T>> unordered_node_set(std::initializer_list<T>, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_node_set<T, Hash, Pred, Allocator>; template<class InputIterator, class Allocator> unordered_node_set(InputIterator, InputIterator, typename see below::size_type, Allocator) -> unordered_node_set<iter-value-type<InputIterator>, boost::hash<iter-value-type<InputIterator>>, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class InputIterator, class Allocator> unordered_node_set(InputIterator, InputIterator, Allocator) -> unordered_node_set<iter-value-type<InputIterator>, boost::hash<iter-value-type<InputIterator>>, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class InputIterator, class Hash, class Allocator> unordered_node_set(InputIterator, InputIterator, typename see below::size_type, Hash, Allocator) -> unordered_node_set<iter-value-type<InputIterator>, Hash, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class T, class Allocator> unordered_node_set(std::initializer_list<T>, typename see below::size_type, Allocator) -> unordered_node_set<T, boost::hash<T>, std::equal_to<T>, Allocator>; template<class T, class Allocator> unordered_node_set(std::initializer_list<T>, Allocator) -> unordered_node_set<T, boost::hash<T>, std::equal_to<T>, Allocator>; template<class T, class Hash, class Allocator> unordered_node_set(std::initializer_list<T>, typename see below::size_type, Hash, Allocator) -> unordered_node_set<T, Hash, std::equal_to<T>, Allocator>; // Equality Comparisons template<class Key, class T, class Hash, class Pred, class Alloc> bool operator!=(const unordered_node_set<Key, T, Hash, Pred, Alloc>& x, const unordered_node_set<Key, T, Hash, Pred, Alloc>& y); template<class Key, class T, class Hash, class Pred, class Alloc> bool operator!=(const unordered_node_set<Key, T, Hash, Pred, Alloc>& x, const unordered_node_set<Key, T, Hash, Pred, Alloc>& y); // swap template<class Key, class T, class Hash, class Pred, class Alloc> void swap(unordered_node_set<Key, T, Hash, Pred, Alloc>& x, unordered_node_set<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y))); // Erasure template<class K, class T, class H, class P, class A, class Predicate> typename unordered_node_set<K, T, H, P, A>::size_type erase_if(unordered_node_set<K, T, H, P, A>& c, Predicate pred); // Pmr aliases (C++17 and up) namespace unordered::pmr { template<class Key, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>> using unordered_node_set = boost::unordered_node_set<Key, Hash, Pred, std::pmr::polymorphic_allocator<Key>>; } }
描述
模板参数
键 |
|
哈希 |
一个一元函数对象类型,充当 |
Pred |
一个二元函数对象,它在 |
分配器 |
一个分配器,其值类型与容器的值类型相同。支持使用花哨指针的分配器。 |
容器的元素节点存储在一个内部的桶数组中。节点插入到由其元素的哈希码确定的桶中,但是如果桶已被占用(发生冲突),则使用原始位置附近的可用桶。
桶数组的大小可以通过调用insert
/emplace
自动增加,或者通过调用rehash
/reserve
来增加。容器的负载因子(元素数量除以桶的数量)永远不会大于max_load_factor()
,除了在小型尺寸下,实现可能决定允许更高的负载。
如果hash_is_avalanching<Hash>::value
为true
,则哈希函数按原样使用;否则,会添加一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。
类型定义
typedef implementation-defined iterator;
一个常量迭代器,其值类型为 value_type
。
迭代器类别至少是前向迭代器。
可转换为 const_iterator
。
typedef implementation-defined const_iterator;
一个常量迭代器,其值类型为 value_type
。
迭代器类别至少是前向迭代器。
typedef implementation-defined node_type;
一个用于保存已提取容器元素的类,模拟NodeHandle。
typedef implementation-defined insert_return_type;
内部类模板的特化
template<class Iterator, class NodeType>
struct insert_return_type // name is exposition only
{
Iterator position;
bool inserted;
NodeType node;
};
其中 Iterator
= iterator
和 NodeType
= node_type
。
构造函数
默认构造函数
unordered_node_set();
使用hasher()
作为哈希函数、key_equal()
作为键相等谓词和allocator_type()
作为分配器来构造一个空容器。
后置条件 |
|
要求 |
如果使用默认值,则 |
桶计数构造函数
explicit unordered_node_set(size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用hf
作为哈希函数、eql
作为键相等谓词和a
作为分配器来构造一个至少包含n
个桶的空容器。
后置条件 |
|
要求 |
如果使用默认值,则 |
迭代器范围构造函数
template<class InputIterator>
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());
使用hf
作为哈希函数、eql
作为键相等谓词和a
作为分配器来构造一个至少包含n
个桶的空容器,并将[f, l)
中的元素插入到其中。
要求 |
如果使用默认值,则 |
复制构造函数
unordered_node_set(unordered_node_set const& other);
复制构造函数。复制包含的元素、哈希函数、谓词和分配器。
如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。
要求 |
`value_type` 可复制构造。 |
移动构造函数
unordered_node_set(unordered_node_set&& other);
移动构造函数。other
的内部桶数组直接转移到新的容器。哈希函数、谓词和分配器从 other
移动构造。如果启用了统计信息 enabled,则将内部统计信息从 other
传输,并调用 other.reset_stats()
。
带分配器的迭代器范围构造函数
template<class InputIterator>
unordered_node_set(InputIterator f, InputIterator l, const allocator_type& a);
使用a
作为分配器、使用默认的哈希函数和键相等谓词来构造一个空容器,并将[f, l)
中的元素插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带分配器的复制构造函数
unordered_node_set(unordered_node_set const& other, Allocator const& a);
构造一个容器,复制other
包含的元素、哈希函数和谓词,但使用分配器a
。
带分配器的移动构造函数
unordered_node_set(unordered_node_set&& other, Allocator const& a);
如果 a == other.get_allocator()
,则 other
的元素节点直接转移到新的容器;否则,元素将从 other
的元素移动构造。哈希函数和谓词从 other
移动构造,分配器从 a
复制构造。如果启用了统计信息 enabled,则当且仅当 a == other.get_allocator()
时,才将内部统计信息从 other
传输,并且始终调用 other.reset_stats()
。
来自 concurrent_node_set 的移动构造函数
unordered_node_set(concurrent_node_set<Key, Hash, Pred, Allocator>&& other);
从 concurrent_node_set
移动构造。other
的内部桶数组直接转移到新的容器。哈希函数、谓词和分配器从 other
移动构造。如果启用了统计信息 enabled,则将内部统计信息从 other
传输,并调用 other.reset_stats()
。
复杂度 |
常数时间。 |
并发性 |
阻塞 |
初始化列表构造函数
unordered_node_set(std::initializer_list<value_type> il,
size_type n = implementation-defined
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用hf
作为哈希函数、eql
作为键相等谓词和a
来构造一个至少包含n
个桶的空容器,并将il
中的元素插入到其中。
要求 |
如果使用默认值,则 |
带分配器的桶计数构造函数
unordered_node_set(size_type n, allocator_type const& a);
使用hf
作为哈希函数、默认的哈希函数和键相等谓词以及a
作为分配器来构造一个至少包含n
个桶的空容器。
后置条件 |
|
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带哈希函数和分配器的桶计数构造函数
unordered_node_set(size_type n, hasher const& hf, allocator_type const& a);
使用hf
作为哈希函数、默认的键相等谓词和a
作为分配器来构造一个至少包含n
个桶的空容器。
后置条件 |
|
要求 |
`key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的迭代器范围构造函数
template<class InputIterator>
unordered_node_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a);
使用a
作为分配器以及默认的哈希函数和键相等谓词来构造一个至少包含n
个桶的空容器,并将[f, l)
中的元素插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和哈希函数的迭代器范围构造函数
template<class InputIterator>
unordered_node_set(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a);
使用hf
作为哈希函数、a
作为分配器以及默认的键相等谓词来构造一个至少包含n
个桶的空容器,并将[f, l)
中的元素插入到其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
带分配器的初始化列表构造函数
unordered_node_set(std::initializer_list<value_type> il, const allocator_type& a);
使用a
以及默认的哈希函数和键相等谓词来构造一个空容器,并将il
中的元素插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的初始化列表构造函数
unordered_node_set(std::initializer_list<value_type> il, size_type n, const allocator_type& a);
使用a
以及默认的哈希函数和键相等谓词来构造一个至少包含n
个桶的空容器,并将il
中的元素插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数、哈希函数和分配器的初始化列表构造函数
unordered_node_set(std::initializer_list<value_type> il, size_type n, const hasher& hf,
const allocator_type& a);
使用hf
作为哈希函数、a
作为分配器以及默认的键相等谓词来构造一个至少包含n
个桶的空容器,并将il
中的元素插入到其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
赋值
复制赋值
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
元素的副本。
要求 |
|
移动赋值
unordered_node_set& operator=(unordered_node_set&& other)
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
std::is_same<pointer, value_type*>::value);
移动赋值运算符。销毁先前存在的元素,交换 other
的哈希函数和谓词,如果 Alloc::propagate_on_container_move_assignment
存在并且 Alloc::propagate_on_container_move_assignment::value
为 true
,则从 other
移动赋值分配器。如果此时分配器等于 other.get_allocator()
,则 other
的内部桶数组直接转移到新的容器;否则,插入 other
元素的移动构造的副本。如果启用了统计信息 enabled,则当且仅当最终分配器等于 other.get_allocator()
时,才将内部统计信息从 other
传输,并且始终调用 other.reset_stats()
。
迭代器
begin
iterator begin() noexcept;
const_iterator begin() const noexcept;
返回值 |
指向容器第一个元素的迭代器,如果容器为空,则返回容器的尾后值。 |
复杂度 |
O( |
cbegin
const_iterator cbegin() const noexcept;
返回值 |
指向容器第一个元素的 `const_iterator`,如果容器为空,则返回容器的尾后值。 |
复杂度 |
O( |
修改器
emplace
template<class... Args> std::pair<iterator, bool> emplace(Args&&... args);
当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。
要求 |
|
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 |
emplace_hint
template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。
position
是关于应插入元素位置的建议。此实现忽略它。
要求 |
|
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 |
复制插入
std::pair<iterator, bool> insert(const value_type& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
要求 |
`value_type` 可 复制插入。 |
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 |
移动插入
std::pair<iterator, bool> insert(value_type&& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
要求 |
`value_type` 可 移动插入。 |
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 |
透明插入
template<class K> std::pair<iterator, bool> insert(K&& k);
当且仅当容器中没有具有等效键的元素时,才将从std::forward<K>(k)
构造的元素插入到容器中。
要求 |
|
返回值 |
返回类型的布尔分量如果进行了插入则为 true。 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 只有当 |
带提示的复制插入
iterator insert(const_iterator hint, const value_type& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
hint
是关于应插入元素位置的建议。此实现忽略它。
要求 |
`value_type` 可 复制插入。 |
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 |
带提示的移动插入
iterator insert(const_iterator hint, value_type&& obj);
当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。
hint
是关于应插入元素位置的建议。此实现忽略它。
要求 |
`value_type` 可 移动插入。 |
返回值 |
返回类型中的 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 |
带提示的透明插入
template<class K> std::pair<iterator, bool> insert(const_iterator hint, K&& k);
当且仅当容器中没有具有等效键的元素时,才将从std::forward<K>(k)
构造的元素插入到容器中。
hint
是关于应插入元素位置的建议。此实现忽略它。
要求 |
|
返回值 |
返回类型的布尔分量如果进行了插入则为 true。 如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 只有当 |
插入迭代器范围
template<class InputIterator> void insert(InputIterator first, InputIterator last);
将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。
要求 |
|
抛出 |
插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 |
插入初始化列表
void insert(std::initializer_list<value_type>);
将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。
要求 |
`value_type` 可 复制插入 到容器中。 |
抛出 |
插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
可能会使迭代器失效,但仅当插入导致负载大于最大负载时。 |
插入节点
insert_return_type insert(node_type&& nh);
如果 nh
不为空,当且仅当容器中没有键与 nh.value()
等效的元素时,才将关联的元素插入到容器中。函数返回时,nh
为空。
返回值 |
从
|
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
如果 |
使用提示插入节点
iterator insert(const_iterator hint, node_type&& nh);
如果 nh
不为空,当且仅当容器中没有键与 nh.value()
等效的元素时,才将关联的元素插入到容器中。如果进行了插入,则 nh
变成空,否则不变。
hint
是关于应插入元素位置的建议。此实现忽略它。
返回值 |
如果 |
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
注释 |
如果 |
按位置删除
convertible-to-iterator erase(iterator position);
convertible-to-iterator erase(const_iterator position);
删除position
指向的元素。
返回值 |
一个不透明对象,可以隐式转换为擦除之前紧跟在 |
抛出 |
无。 |
注释 |
返回的不透明对象只能被丢弃或立即转换为 |
按键删除
size_type erase(const key_type& k);
template<class K> size_type erase(K&& k);
删除所有键值等效于k
的元素。
返回值 |
删除的元素数量。 |
抛出 |
只有当 |
注释 |
只有当 |
删除范围
iterator erase(const_iterator first, const_iterator last);
删除从first
到last
范围内的元素。
返回值 |
删除元素之后的迭代器 - 即 |
抛出 |
此实现中没有任何操作(既不调用 |
交换 (swap)
void swap(unordered_node_set& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_swap::value);
将容器的内容与参数交换。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。
抛出 |
除非 |
按位置提取
node_type extract(const_iterator position);
提取 position
指向的元素。
返回值 |
包含提取元素的 |
抛出 |
无。 |
通过键提取
node_type extract(const key_type& k);
template<class K> node_type extract(K&& k);
提取键与 k
等效的元素(如果存在)。
返回值 |
包含提取元素的 |
抛出 |
只有当 |
注释 |
只有当 |
清空 (clear)
void clear() noexcept;
删除容器中的所有元素。
后置条件 |
|
合并 (merge)
template<class H2, class P2>
void merge(unordered_node_set<Key, T, H2, P2, Allocator>& source);
template<class H2, class P2>
void merge(unordered_node_set<Key, T, H2, P2, Allocator>&& source);
从 source
传输所有键在 *this
中不存在的元素节点。
要求 |
|
注释 |
使指向已传输元素的迭代器失效。如果 |
查找
查找 (find)
iterator find(const key_type& k);
const_iterator find(const key_type& k) const;
template<class K>
iterator find(const K& k);
返回值 |
指向具有与 |
注释 |
只有当 |
计数 (count)
size_type count(const key_type& k) const;
template<class K>
size_type count(const K& k) const;
返回值 |
键值等效于 |
注释 |
只有当 |
包含 (contains)
bool contains(const key_type& k) const;
template<class K>
bool contains(const K& k) const;
返回值 |
一个布尔值,指示容器中是否存在键等于 |
注释 |
只有当 |
相等范围 (equal_range)
std::pair<iterator, iterator> equal_range(const key_type& k);
std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
template<class K>
std::pair<iterator, iterator> equal_range(const K& k);
template<class K>
std::pair<const_iterator, const_iterator> equal_range(const K& k) const;
返回值 |
包含所有键值等效于 |
注释 |
只有当 |
哈希策略
负载因子 (load_factor)
float load_factor() const noexcept;
返回值 |
|
设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果 |
不执行任何操作,因为不允许用户更改此参数。为了与 |
最大负载
size_type max_load() const noexcept;
返回值 |
容器在不重新哈希的情况下可以容纳的最大元素数量,假设不再删除元素。 |
注意 |
构造、重新哈希或清除后,容器的最大负载至少为 |
重新哈希 (rehash)
void rehash(size_type n);
如有必要,更改桶数组的大小,使其至少包含 n
个桶,并且负载因子小于或等于最大负载因子。在适用情况下,这将扩大或缩小与容器关联的 bucket_count()
。
当 size() == 0
时,rehash(0)
将释放底层桶数组。如果提供的分配器使用高级指针,则随后将执行默认分配。
使迭代器失效并更改元素的顺序。
抛出 |
除非容器的哈希函数或比较函数抛出异常,否则该函数无效。 |
预留空间 (reserve)
void reserve(size_type n);
等效于 a.rehash(ceil(n / a.max_load_factor()))
。
与rehash
类似,此函数可用于增加或减少容器中桶的数量。
使迭代器失效并更改元素的顺序。
抛出 |
除非容器的哈希函数或比较函数抛出异常,否则该函数无效。 |
推导指南
如果出现以下任何情况,则推导指南不会参与重载解析:
-
它具有
InputIterator
模板参数,并且为该参数推导的类型不符合输入迭代器的资格。 -
它具有
Allocator
模板参数,并且为该参数推导的类型不符合分配器的资格。 -
它具有
Hash
模板参数,并且为该参数推导的类型为整数类型或符合分配器资格的类型。 -
它具有
Pred
模板参数,并且为该参数推导的类型符合分配器资格的类型。
推导指南中的size_type
参数类型引用由推导指南推导出的容器类型的size_type
成员类型。其默认值与所选构造函数的默认值一致。
迭代器值类型 (iter-value-type)
template<class InputIterator> using iter-value-type = typename std::iterator_traits<InputIterator>::value_type; // exposition only
相等比较
相等运算符 (operator==)
template<class Key, class T, class Hash, class Pred, class Alloc>
bool operator==(const unordered_node_set<Key, T, Hash, Pred, Alloc>& x,
const unordered_node_set<Key, T, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个具有相同键和相同值(使用operator==
比较值类型)的元素,则返回true
。
注释 |
如果两个容器没有等效的相等谓词,则行为未定义。 |
不相等运算符 (operator!=)
template<class Key, class T, class Hash, class Pred, class Alloc>
bool operator!=(const unordered_node_set<Key, T, Hash, Pred, Alloc>& x,
const unordered_node_set<Key, T, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个具有相同键和相同值(使用operator==
比较值类型)的元素,则返回false
。
注释 |
如果两个容器没有等效的相等谓词,则行为未定义。 |
交换
template<class Key, class T, class Hash, class Pred, class Alloc>
void swap(unordered_node_set<Key, T, Hash, Pred, Alloc>& x,
unordered_node_set<Key, T, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
交换x
和y
的内容。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。
效果 |
|
抛出 |
除非 |
erase_if
template<class K, class T, class H, class P, class A, class Predicate>
typename unordered_node_set<K, T, H, P, A>::size_type
erase_if(unordered_node_set<K, T, H, P, A>& c, Predicate pred);
遍历容器c
,并移除所有满足提供的谓词返回true
的元素。
返回值 |
被移除元素的数量。 |
注释 |
等价于
|
序列化
可以使用此库提供的 API,通过 Boost.Serialization 存档/检索 unordered_node_set
。支持常规存档和 XML 存档。
将 unordered_node_set 保存到存档
将 unordered_node_set
x
的所有元素保存到存档(XML 存档)ar
中。
要求 |
|
从存档加载 unordered_node_set
删除 unordered_node_set
x
中所有预先存在的元素,并从存档(XML 存档)ar
中插入原始 unordered_node_set
other
的元素的已恢复副本(这些元素已保存到 ar
读取的存储区中)。
要求 |
|
将迭代器/const_iterator保存到存档
将iterator
(const_iterator
)it
的位置信息保存到存档(XML存档)ar
中。it
可以是end()
迭代器。
要求 |
|
从存档加载迭代器/const_iterator
使iterator
(const_iterator
)it
指向原始iterator
(const_iterator
)在存档(XML存档)ar
读取的存储中保存的已恢复位置。
要求 |
如果 |
类模板 concurrent_flat_map
boost::concurrent_flat_map
— 一个哈希表,它将唯一键与另一个值关联起来,并允许在没有外部同步机制的情况下并发插入、删除、查找和访问元素。
尽管 `boost::concurrent_flat_map` 充当容器的角色,但它并不符合标准 C++ 容器 概念。特别是,它不提供迭代器和相关的操作(`begin`、`end` 等)。元素的访问和修改是通过用户提供的访问函数来完成的,这些函数被传递给 `concurrent_flat_map` 的操作,并在内部以受控的方式执行。这种基于访问的 API 允许低争用并发使用场景。
boost::concurrent_flat_map
的内部数据结构类似于 `boost::unordered_flat_map`。由于它使用开放寻址技术,`value_type` 必须是可移动构造的,并且在重新哈希时不会保持指针稳定性。
概要
// #include <boost/unordered/concurrent_flat_map.hpp> namespace boost { template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T>>> class concurrent_flat_map { public: // types using key_type = Key; using mapped_type = T; using value_type = std::pair<const Key, T>; using init_type = std::pair< typename std::remove_const<Key>::type, typename std::remove_const<T>::type >; using hasher = Hash; using key_equal = Pred; using allocator_type = Allocator; using pointer = typename std::allocator_traits<Allocator>::pointer; using const_pointer = typename std::allocator_traits<Allocator>::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 = stats-type; // if statistics are enabled // constants static constexpr size_type bulk_visit_size = implementation-defined; // construct/copy/destroy concurrent_flat_map(); explicit concurrent_flat_map(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); template<class InputIterator> 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()); concurrent_flat_map(const concurrent_flat_map& other); concurrent_flat_map(concurrent_flat_map&& other); template<class InputIterator> concurrent_flat_map(InputIterator f, InputIterator l,const allocator_type& a); explicit concurrent_flat_map(const Allocator& a); concurrent_flat_map(const concurrent_flat_map& other, const Allocator& a); concurrent_flat_map(concurrent_flat_map&& other, const Allocator& a); concurrent_flat_map(unordered_flat_map<Key, T, Hash, Pred, Allocator>&& other); concurrent_flat_map(std::initializer_list<value_type> il, size_type n = implementation-defined const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); concurrent_flat_map(size_type n, const allocator_type& a); concurrent_flat_map(size_type n, const hasher& hf, const allocator_type& a); template<class InputIterator> concurrent_flat_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a); template<class InputIterator> concurrent_flat_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a); concurrent_flat_map(std::initializer_list<value_type> il, const allocator_type& a); concurrent_flat_map(std::initializer_list<value_type> il, size_type n, const allocator_type& a); concurrent_flat_map(std::initializer_list<value_type> il, size_type n, const hasher& hf, const allocator_type& a); ~concurrent_flat_map(); concurrent_flat_map& operator=(const concurrent_flat_map& other); concurrent_flat_map& operator=(concurrent_flat_map&& other) noexcept( (boost::allocator_traits<Allocator>::is_always_equal::value || boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) && std::is_same<pointer, value_type*>::value); concurrent_flat_map& operator=(std::initializer_list<value_type>); allocator_type get_allocator() const noexcept; // visitation template<class F> size_t visit(const key_type& k, F f); template<class F> size_t visit(const key_type& k, F f) const; template<class F> size_t cvisit(const key_type& k, F f) const; template<class K, class F> size_t visit(const K& k, F f); template<class K, class F> size_t visit(const K& k, F f) const; template<class K, class F> size_t cvisit(const K& k, F f) const; template<class FwdIterator, class F> size_t visit(FwdIterator first, FwdIterator last, F f); template<class FwdIterator, class F> size_t visit(FwdIterator first, FwdIterator last, F f) const; template<class FwdIterator, class F> size_t cvisit(FwdIterator first, FwdIterator last, F f) const; template<class F> size_t visit_all(F f); template<class F> size_t visit_all(F f) const; template<class F> size_t cvisit_all(F f) const; template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f); template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f) const; template<class ExecutionPolicy, class F> void cvisit_all(ExecutionPolicy&& policy, F f) const; template<class F> bool visit_while(F f); template<class F> bool visit_while(F f) const; template<class F> bool cvisit_while(F f) const; template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f); template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f) const; template<class ExecutionPolicy, class F> bool cvisit_while(ExecutionPolicy&& policy, F f) const; // capacity [[nodiscard]] bool empty() const noexcept; size_type size() const noexcept; size_type max_size() const noexcept; // modifiers template<class... Args> bool emplace(Args&&... args); bool insert(const value_type& obj); bool insert(const init_type& obj); bool insert(value_type&& obj); bool insert(init_type&& obj); template<class InputIterator> size_type insert(InputIterator first, InputIterator last); size_type insert(std::initializer_list<value_type> il); template<class... Args, class F> bool emplace_or_visit(Args&&... args, F&& f); template<class... Args, class F> bool emplace_or_cvisit(Args&&... args, F&& f); template<class F> bool insert_or_visit(const value_type& obj, F f); template<class F> bool insert_or_cvisit(const value_type& obj, F f); template<class F> bool insert_or_visit(const init_type& obj, F f); template<class F> bool insert_or_cvisit(const init_type& obj, F f); template<class F> bool insert_or_visit(value_type&& obj, F f); template<class F> bool insert_or_cvisit(value_type&& obj, F f); template<class F> bool insert_or_visit(init_type&& obj, F f); template<class F> bool insert_or_cvisit(init_type&& obj, F f); template<class InputIterator,class F> size_type insert_or_visit(InputIterator first, InputIterator last, F f); template<class InputIterator,class F> size_type insert_or_cvisit(InputIterator first, InputIterator last, F f); template<class F> size_type insert_or_visit(std::initializer_list<value_type> il, F f); template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> il, F f); template<class... Args, class F1, class F2> bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2); template<class... Args, class F1, class F2> bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2); template<class F1, class F2> bool insert_and_visit(const value_type& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_visit(const init_type& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_cvisit(const init_type& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_visit(value_type&& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_visit(init_type&& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2); template<class InputIterator,class F1, class F2> size_type insert_and_visit(InputIterator first, InputIterator last, F1 f1, F2 f2); template<class InputIterator,class F1, class F2> size_type insert_and_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2); template<class F1, class F2> size_type insert_and_visit(std::initializer_list<value_type> il, F1 f1, F2 f2); template<class F1, class F2> size_type insert_and_cvisit(std::initializer_list<value_type> il, F1 f1, F2 f2); template<class... Args> bool try_emplace(const key_type& k, Args&&... args); template<class... Args> bool try_emplace(key_type&& k, Args&&... args); template<class K, class... Args> bool try_emplace(K&& k, Args&&... args); template<class... Args, class F> bool try_emplace_or_visit(const key_type& k, Args&&... args, F&& f); template<class... Args, class F> bool try_emplace_or_cvisit(const key_type& k, Args&&... args, F&& f); template<class... Args, class F> bool try_emplace_or_visit(key_type&& k, Args&&... args, F&& f); template<class... Args, class F> bool try_emplace_or_cvisit(key_type&& k, Args&&... args, F&& f); template<class K, class... Args, class F> bool try_emplace_or_visit(K&& k, Args&&... args, F&& f); template<class K, class... Args, class F> bool try_emplace_or_cvisit(K&& k, Args&&... args, F&& f); template<class... Args, class F1, class F2> bool try_emplace_and_visit(const key_type& k, Args&&... args, F1&& f1, F2&& f2); template<class... Args, class F1, class F2> bool try_emplace_and_cvisit(const key_type& k, Args&&... args, F1&& f1, F2&& f2); template<class... Args, class F1, class F2> bool try_emplace_and_visit(key_type&& k, Args&&... args, F1&& f1, F2&& f2); template<class... Args, class F1, class F2> bool try_emplace_and_cvisit(key_type&& k, Args&&... args, F1&& f1, F2&& f2); template<class K, class... Args, class F1, class F2> bool try_emplace_and_visit(K&& k, Args&&... args, F1&& f1, F2&& f2); template<class K, class... Args, class F1, class F2> bool try_emplace_and_cvisit(K&& k, Args&&... args, F1&& f1, F2&& f2); template<class M> bool insert_or_assign(const key_type& k, M&& obj); template<class M> bool insert_or_assign(key_type&& k, M&& obj); template<class K, class M> bool insert_or_assign(K&& k, M&& obj); size_type erase(const key_type& k); template<class K> size_type erase(const K& k); template<class F> size_type erase_if(const key_type& k, F f); template<class K, class F> size_type erase_if(const K& k, F f); template<class F> size_type erase_if(F f); template<class ExecutionPolicy, class F> void erase_if(ExecutionPolicy&& policy, F f); void swap(concurrent_flat_map& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || boost::allocator_traits<Allocator>::propagate_on_container_swap::value); void clear() noexcept; template<class H2, class P2> size_type merge(concurrent_flat_map<Key, T, H2, P2, Allocator>& source); template<class H2, class P2> size_type merge(concurrent_flat_map<Key, T, H2, P2, Allocator>&& source); // observers hasher hash_function() const; key_equal key_eq() const; // map operations size_type count(const key_type& k) const; template<class K> size_type count(const K& k) const; bool contains(const key_type& k) const; template<class K> bool contains(const K& k) const; // bucket interface size_type bucket_count() const noexcept; // hash policy float load_factor() const noexcept; float max_load_factor() const noexcept; void max_load_factor(float z); size_type max_load() const noexcept; void rehash(size_type n); void reserve(size_type n); // statistics (if enabled) stats get_stats() const; void reset_stats() noexcept; }; // Deduction Guides template<class InputIterator, class Hash = boost::hash<iter-key-type<InputIterator>>, class Pred = std::equal_to<iter-key-type<InputIterator>>, class Allocator = std::allocator<iter-to-alloc-type<InputIterator>>> concurrent_flat_map(InputIterator, InputIterator, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> concurrent_flat_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, Hash, Pred, Allocator>; template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T>>> concurrent_flat_map(std::initializer_list<std::pair<Key, T>>, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> concurrent_flat_map<Key, T, Hash, Pred, Allocator>; template<class InputIterator, class Allocator> concurrent_flat_map(InputIterator, InputIterator, typename see below::size_type, Allocator) -> concurrent_flat_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, boost::hash<iter-key-type<InputIterator>>, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class InputIterator, class Allocator> concurrent_flat_map(InputIterator, InputIterator, Allocator) -> concurrent_flat_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, boost::hash<iter-key-type<InputIterator>>, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class InputIterator, class Hash, class Allocator> concurrent_flat_map(InputIterator, InputIterator, typename see below::size_type, Hash, Allocator) -> concurrent_flat_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, Hash, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class Key, class T, class Allocator> concurrent_flat_map(std::initializer_list<std::pair<Key, T>>, typename see below::size_type, Allocator) -> concurrent_flat_map<Key, T, boost::hash<Key>, std::equal_to<Key>, Allocator>; template<class Key, class T, class Allocator> concurrent_flat_map(std::initializer_list<std::pair<Key, T>>, Allocator) -> concurrent_flat_map<Key, T, boost::hash<Key>, std::equal_to<Key>, Allocator>; template<class Key, class T, class Hash, class Allocator> concurrent_flat_map(std::initializer_list<std::pair<Key, T>>, typename see below::size_type, Hash, Allocator) -> concurrent_flat_map<Key, T, Hash, std::equal_to<Key>, Allocator>; // Equality Comparisons template<class Key, class T, class Hash, class Pred, class Alloc> bool operator==(const concurrent_flat_map<Key, T, Hash, Pred, Alloc>& x, const concurrent_flat_map<Key, T, Hash, Pred, Alloc>& y); template<class Key, class T, class Hash, class Pred, class Alloc> bool operator!=(const concurrent_flat_map<Key, T, Hash, Pred, Alloc>& x, const concurrent_flat_map<Key, T, Hash, Pred, Alloc>& y); // swap template<class Key, class T, class Hash, class Pred, class Alloc> void swap(concurrent_flat_map<Key, T, Hash, Pred, Alloc>& x, concurrent_flat_map<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y))); // Erasure template<class K, class T, class H, class P, class A, class Predicate> typename concurrent_flat_map<K, T, H, P, A>::size_type erase_if(concurrent_flat_map<K, T, H, P, A>& c, Predicate pred); // Pmr aliases (C++17 and up) namespace unordered::pmr { template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>> using concurrent_flat_map = boost::concurrent_flat_map<Key, T, Hash, Pred, std::pmr::polymorphic_allocator<std::pair<const Key, T>>>; } }
描述
模板参数
键 |
`Key` 和 `T` 必须是 可移动构造的。`std::pair<const Key, T>` 必须能够从任何可转换为它的 `std::pair` 对象 就地构造 到表中,并且它也必须能够从表中 删除。 |
T |
|
哈希 |
一个一元函数对象类型,充当 |
Pred |
一个二元函数对象,它在 |
分配器 |
一个值类型与表的值类型相同的分配器。支持使用 花哨指针 的分配器。 |
表的元素保存在内部的桶数组中。元素插入到由其哈希码确定的桶中,但如果该桶已被占用(发生冲突),则使用原始位置附近的一个可用桶。
桶数组的大小可以通过调用 `insert`/`emplace` 自动增加,或者通过调用 `rehash`/`reserve` 来增加。表的负载因子(元素数量除以桶的数量)永远不会大于 `max_load_factor()`,除非在较小的尺寸下,实现可能决定允许更高的负载。
如果hash_is_avalanching<Hash>::value
为true
,则哈希函数按原样使用;否则,会添加一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。
并发需求和保证
对同一 `Hash` 或 `Pred` 常量实例的并发调用 `operator()` 不会引入数据竞争。对于 `Alloc` 是 `Allocator` 或从 `Allocator` 重新绑定的任何分配器类型,对同一 `Alloc` 实例 `al` 的以下操作的并发调用不会引入数据竞争。
-
从 `al` 复制构造从 `Alloc` 重新绑定的分配器
-
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` 的非 const 容器函数中,针对最后一个(或唯一一个)访问函数。
-
任何插入或修改元素 `e` 的 `boost::concurrent_flat_map` 操作都与对 `e` 的访问函数的内部调用同步。
由 `boost::concurrent_flat_map` `x` 执行的访问函数不允许调用 `x` 上的任何操作;仅当 `y` 上并发未完成的操作不会直接或间接访问 `x` 时,才允许调用另一个 `boost::concurrent_flat_map` 实例 `y` 上的操作。
配置宏
BOOST_UNORDERED_DISABLE_REENTRANCY_CHECK
在调试版本中(更准确地说,当 BOOST_ASSERT_IS_VOID
未定义时),会检测到容器重入(从访问 `m` 元素的函数中非法调用 `m` 上的操作)并通过 `BOOST_ASSERT_MSG` 发出信号。当运行时速度成为关注点时,可以通过全局定义此宏来禁用此功能。
构造函数
默认构造函数
concurrent_flat_map();
使用 `hasher()` 作为哈希函数、`key_equal()` 作为键相等谓词和 `allocator_type()` 作为分配器构造一个空表。
后置条件 |
|
要求 |
如果使用默认值,则 |
桶计数构造函数
explicit concurrent_flat_map(size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用 `hf` 作为哈希函数、`eql` 作为键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。
后置条件 |
|
要求 |
如果使用默认值,则 |
迭代器范围构造函数
template<class InputIterator>
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());
使用 `hf` 作为哈希函数、`eql` 作为键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表,并将元素从 `[f, l)` 插入到其中。
要求 |
如果使用默认值,则 |
复制构造函数
concurrent_flat_map(concurrent_flat_map const& other);
复制构造函数。复制包含的元素、哈希函数、谓词和分配器。
如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。
要求 |
`value_type` 可复制构造。 |
并发性 |
阻塞 |
移动构造函数
concurrent_flat_map(concurrent_flat_map&& other);
移动构造函数。`other` 的内部桶数组直接转移到新表。哈希函数、谓词和分配器是从 `other` 移动构造的。如果启用了统计 功能,则将内部统计信息从 `other` 传输并调用 `other.reset_stats()`。
并发性 |
阻塞 |
带分配器的迭代器范围构造函数
template<class InputIterator>
concurrent_flat_map(InputIterator f, InputIterator l, const allocator_type& a);
使用 `a` 作为分配器,使用默认哈希函数和键相等谓词构造一个空表,并将元素从 `[f, l)` 插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带分配器的复制构造函数
concurrent_flat_map(concurrent_flat_map const& other, Allocator const& a);
构造一个表,复制 `other` 的包含元素、哈希函数和谓词,但使用分配器 `a`。
并发性 |
阻塞 |
带分配器的移动构造函数
concurrent_flat_map(concurrent_flat_map&& other, Allocator const& a);
如果 `a == other.get_allocator()`,则 `other` 的元素直接转移到新表;否则,元素是从 `other` 的元素移动构造的。哈希函数和谓词是从 `other` 移动构造的,分配器是从 `a` 复制构造的。如果启用了统计 功能,则当且仅当 `a == other.get_allocator()` 时,才将内部统计信息从 `other` 传输,并且始终调用 `other.reset_stats()`。
并发性 |
阻塞 |
从 unordered_flat_map 的移动构造函数
concurrent_flat_map(unordered_flat_map<Key, T, Hash, Pred, Allocator>&& other);
从 unordered_flat_map
移动构造。`other` 的内部桶数组直接转移到新容器。哈希函数、谓词和分配器是从 `other` 移动构造的。如果启用了统计 功能,则将内部统计信息从 `other` 传输并调用 `other.reset_stats()`。
复杂度 |
O( |
初始化列表构造函数
concurrent_flat_map(std::initializer_list<value_type> il,
size_type n = implementation-defined
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用 `hf` 作为哈希函数、`eql` 作为键相等谓词和 `a` 构造一个至少具有 `n` 个桶的空表,并将元素从 `il` 插入到其中。
要求 |
如果使用默认值,则 |
带分配器的桶计数构造函数
concurrent_flat_map(size_type n, allocator_type const& a);
使用 `hf` 作为哈希函数、默认哈希函数和键相等谓词以及 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。
后置条件 |
|
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带哈希函数和分配器的桶计数构造函数
concurrent_flat_map(size_type n, hasher const& hf, allocator_type const& a);
使用 `hf` 作为哈希函数、默认键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。
后置条件 |
|
要求 |
`key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的迭代器范围构造函数
template<class InputIterator>
concurrent_flat_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a);
使用 `a` 作为分配器和默认哈希函数和键相等谓词构造一个至少具有 `n` 个桶的空表,并将元素从 `[f, l)` 插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和哈希函数的迭代器范围构造函数
template<class InputIterator>
concurrent_flat_map(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a);
使用 `hf` 作为哈希函数、`a` 作为分配器、默认键相等谓词构造一个至少具有 `n` 个桶的空表,并将元素从 `[f, l)` 插入到其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
带分配器的初始化列表构造函数
concurrent_flat_map(std::initializer_list<value_type> il, const allocator_type& a);
使用 `a` 和默认哈希函数和键相等谓词构造一个空表,并将元素从 `il` 插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的初始化列表构造函数
concurrent_flat_map(std::initializer_list<value_type> il, size_type n, const allocator_type& a);
使用 `a` 和默认哈希函数和键相等谓词构造一个至少具有 `n` 个桶的空表,并将元素从 `il` 插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数、哈希函数和分配器的初始化列表构造函数
concurrent_flat_map(std::initializer_list<value_type> il, size_type n, const hasher& hf,
const allocator_type& a);
使用 `hf` 作为哈希函数、`a` 作为分配器和默认键相等谓词构造一个至少具有 `n` 个桶的空表,并将元素从 `il` 插入到其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
赋值
复制赋值
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
元素的副本。
要求 |
|
并发性 |
阻塞于 `*this` 和 `other`。 |
移动赋值
concurrent_flat_map& operator=(concurrent_flat_map&& other)
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
std::is_same<pointer, value_type*>::value);
移动赋值运算符。销毁先前存在的元素,交换 `other` 的哈希函数和谓词,如果 `Alloc::propagate_on_container_move_assignment` 存在且 `Alloc::propagate_on_container_move_assignment::value` 为 `true`,则从 `other` 移动赋值分配器。如果此时分配器等于 `other.get_allocator()`,则 `other` 的内部桶数组直接转移到 `*this`;否则,插入 `other` 元素的移动构造的副本。如果启用了统计 功能,则当且仅当最终分配器等于 `other.get_allocator()` 时,才将内部统计信息从 `other` 传输,并且始终调用 `other.reset_stats()`。
并发性 |
阻塞于 `*this` 和 `other`。 |
访问
[c]visit
template<class F> size_t visit(const key_type& k, F f);
template<class F> size_t visit(const key_type& k, F f) const;
template<class F> size_t cvisit(const key_type& k, F f) const;
template<class K, class F> size_t visit(const K& k, F f);
template<class K, class F> size_t visit(const K& k, F f) const;
template<class K, class F> size_t cvisit(const K& k, F f) const;
如果存在键与 `k` 等效的元素 `x`,则使用对 `x` 的引用调用 `f`。当且仅当 `*this` 为 const 时,此引用才是 const。
返回值 |
访问的元素数量(0 或 1)。 |
注释 |
只有当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义时,`template<class K, class F>` 重载才会参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这允许异构查找,避免了实例化 `Key` 类型实例的成本。 |
批量访问
template<class FwdIterator, class F>
size_t visit(FwdIterator first, FwdIterator last, F f);
template<class FwdIterator, class F>
size_t visit(FwdIterator first, FwdIterator last, F f) const;
template<class FwdIterator, class F>
size_t cvisit(FwdIterator first, FwdIterator last, F f) const;
对于范围 [first
, last
) 中的每个元素 `k`,如果容器中存在键与 `k` 等效的元素 `x`,则使用对 `x` 的引用调用 `f`。当且仅当 `*this` 为 const 时,此引用才是 const。
虽然在功能上等效于对每个键分别调用[c]visit
,但批量访问由于内部优化而通常更快。建议std::distance(first,last)
至少为bulk_visit_size
才能获得性能提升:超过此大小,性能预计不会进一步提高。
要求 |
|
返回值 |
已访问元素的数量。 |
[c]visit_all
template<class F> size_t visit_all(F f);
template<class F> size_t visit_all(F f) const;
template<class F> size_t cvisit_all(F f) const;
连续地使用对表中每个元素的引用调用f
。只有当*this
是常量时,这些引用才是常量。
返回值 |
已访问元素的数量。 |
并行 [c]visit_all
template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f);
template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f) const;
template<class ExecutionPolicy, class F> void cvisit_all(ExecutionPolicy&& policy, F f) const;
使用对表中每个元素的引用调用f
。只有当*this
是常量时,这些引用才是常量。根据指定的执行策略的语义并行执行。
抛出 |
根据所用执行策略的异常处理机制,如果在 |
注释 |
仅在支持C++17并行算法的编译器中可用。 只有当 不允许无序执行策略。 |
[c]visit_while
template<class F> bool visit_while(F f);
template<class F> bool visit_while(F f) const;
template<class F> bool cvisit_while(F f) const;
连续地使用对表中每个元素的引用调用f
,直到f
返回false
或访问所有元素。只有当*this
是常量时,这些元素引用才是常量。
返回值 |
如果 |
并行 [c]visit_while
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f);
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f) const;
template<class ExecutionPolicy, class F> bool cvisit_while(ExecutionPolicy&& policy, F f) const;
调用f
,使用对表中每个元素的引用,直到f
返回false
或访问所有元素。只有当*this
是常量时,这些元素引用才是常量。根据指定的执行策略的语义并行执行。
返回值 |
如果 |
抛出 |
根据所用执行策略的异常处理机制,如果在 |
注释 |
仅在支持C++17并行算法的编译器中可用。 只有当 不允许无序执行策略。 并行化意味着执行并不一定在 |
修改器
emplace
template<class... Args> bool emplace(Args&&... args);
仅当表中不存在具有等效键的元素时,才使用参数args
构造的对象插入到表中。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 如果 |
复制插入
bool insert(const value_type& obj);
bool insert(const init_type& obj);
仅当表中不存在具有等效键的元素时,才将obj
插入到表中。
要求 |
`value_type` 可 复制插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 当 |
移动插入
bool insert(value_type&& obj);
bool insert(init_type&& obj);
仅当表中不存在具有等效键的元素时,才将obj
插入到表中。
要求 |
`value_type` 可 移动插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 当 |
插入迭代器范围
template<class InputIterator> size_type insert(InputIterator first, InputIterator last);
等价于
while(first != last) this->emplace(*first++);
返回值 |
插入的元素数量。 |
插入初始化列表
size_type insert(std::initializer_list<value_type> il);
等价于
this->insert(il.begin(), il.end());
返回值 |
插入的元素数量。 |
emplace_or_[c]visit
template<class... Args, class F> bool emplace_or_visit(Args&&... args, F&& f);
template<class... Args, class F> bool emplace_or_cvisit(Args&&... args, F&& f);
如果表中不存在具有等效键的元素,则使用参数args
构造的对象插入到表中。否则,使用对等效元素的引用调用f
;如果使用的是emplace_or_cvisit
,则该引用为常量。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 该接口仅用于说明,因为C++不允许在可变参数包之后声明参数 |
复制 insert_or_[c]visit
template<class F> bool insert_or_visit(const value_type& obj, F f);
template<class F> bool insert_or_cvisit(const value_type& obj, F f);
template<class F> bool insert_or_visit(const init_type& obj, F f);
template<class F> bool insert_or_cvisit(const init_type& obj, F f);
仅当表中不存在具有等效键的元素时,才将obj
插入到表中。否则,使用对等效元素的引用调用f
;只有当使用*_cvisit
重载时,该引用才是常量。
要求 |
`value_type` 可 复制插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 在 |
移动 insert_or_[c]visit
template<class F> bool insert_or_visit(value_type&& obj, F f);
template<class F> bool insert_or_cvisit(value_type&& obj, F f);
template<class F> bool insert_or_visit(init_type&& obj, F f);
template<class F> bool insert_or_cvisit(init_type&& obj, F f);
仅当表中不存在具有等效键的元素时,才将obj
插入到表中。否则,使用对等效元素的引用调用f
;只有当使用*_cvisit
重载时,该引用才是常量。
要求 |
`value_type` 可 移动插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 在 |
插入迭代器范围或访问
template<class InputIterator,class F>
size_type insert_or_visit(InputIterator first, InputIterator last, F f);
template<class InputIterator,class F>
size_type insert_or_cvisit(InputIterator first, InputIterator last, F f);
等价于
while(first != last) this->emplace_or_[c]visit(*first++, f);
返回值 |
插入的元素数量。 |
插入初始化列表或访问
template<class F> size_type insert_or_visit(std::initializer_list<value_type> il, F f);
template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> il, F f);
等价于
this->insert_or_[c]visit(il.begin(), il.end(), std::ref(f));
返回值 |
插入的元素数量。 |
emplace_and_[c]visit
template<class... Args, class F1, class F2>
bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2);
如果表中不存在具有等效键的元素,则使用参数args
构造的对象插入到表中,然后使用对新创建元素的非常量引用调用f1
。否则,使用对等效元素的引用调用f2
;如果使用的是emplace_and_cvisit
,则该引用为常量。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 该接口仅用于说明,因为C++不允许在可变参数包之后声明参数 |
复制 insert_and_[c]visit
template<class F1, class F2> bool insert_and_visit(const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_visit(const init_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(const init_type& obj, F1 f1, F2 f2);
仅当表中不存在具有等效键的元素时,才将obj
插入到表中,然后使用对新创建元素的非常量引用调用f1
。否则,使用对等效元素的引用调用f2
;只有当使用*_cvisit
重载时,该引用才是常量。
要求 |
`value_type` 可 复制插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 在 |
移动 insert_and_[c]visit
template<class F1, class F2> bool insert_and_visit(value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_visit(init_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2);
仅当表中不存在具有等效键的元素时,才将obj
插入到表中,然后使用对新创建元素的非常量引用调用f1
。否则,使用对等效元素的引用调用f2
;只有当使用*_cvisit
重载时,该引用才是常量。
要求 |
`value_type` 可 移动插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 在 |
插入迭代器范围和访问
template<class InputIterator, class F1, class F2>
size_type insert_or_visit(InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class InputIterator, class F1, class F2>
size_type insert_or_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2);
等价于
while(first != last) this->emplace_and_[c]visit(*first++, f1, f2);
返回值 |
插入的元素数量。 |
插入初始化列表和访问
template<class F1, class F2>
size_type insert_and_visit(std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
size_type insert_and_cvisit(std::initializer_list<value_type> il, F1 f1, F2 f2);
等价于
this->insert_and_[c]visit(il.begin(), il.end(), std::ref(f1), std::ref(f2));
返回值 |
插入的元素数量。 |
try_emplace
template<class... Args> bool try_emplace(const key_type& k, Args&&... args);
template<class... Args> bool try_emplace(key_type&& k, Args&&... args);
template<class K, class... Args> bool try_emplace(K&& k, Args&&... args);
如果表中不存在具有键k
的现有元素,则将使用k
和args
构造的元素插入到表中。
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
此函数类似于emplace,不同之处在于,如果存在具有等效键的元素,则不会构造
与emplace不同,它只是将所有参数转发到 如果发出重新哈希,则使指向元素的指针和引用无效。 只有当 |
try_emplace_or_[c]visit
template<class... Args, class F>
bool try_emplace_or_visit(const key_type& k, Args&&... args, F&& f);
template<class... Args, class F>
bool try_emplace_or_cvisit(const key_type& k, Args&&... args, F&& f);
template<class... Args, class F>
bool try_emplace_or_visit(key_type&& k, Args&&... args, F&& f);
template<class... Args, class F>
bool try_emplace_or_cvisit(key_type&& k, Args&&... args, F&& f);
template<class K, class... Args, class F>
bool try_emplace_or_visit(K&& k, Args&&... args, F&& f);
template<class K, class... Args, class F>
bool try_emplace_or_cvisit(K&& k, Args&&... args, F&& f);
如果表中不存在具有键k
的现有元素,则将使用k
和args
构造的元素插入到表中。否则,使用对等效元素的引用调用f
;只有当使用*_cvisit
重载时,该引用才是常量。
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果存在具有等效键的元素,则不会构造
如果发出重新哈希,则使指向元素的指针和引用无效。 该接口仅用于说明,因为C++不允许在可变参数包之后声明参数 只有当 |
try_emplace_and_[c]visit
template<class... Args, class F1, class F2>
bool try_emplace_and_visit(const key_type& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool try_emplace_and_cvisit(const key_type& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool try_emplace_and_visit(key_type&& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool try_emplace_and_cvisit(key_type&& k, Args&&... args, F1&& f1, F2&& f2);
template<class K, class... Args, class F1, class F2>
bool try_emplace_and_visit(K&& k, Args&&... args, F1&& f1, F2&& f2);
template<class K, class... Args, class F1, class F2>
bool try_emplace_and_cvisit(K&& k, Args&&... args, F1&& f1, F2&& f2);
如果表中不存在具有键k
的现有元素,则将使用k
和args
构造的元素插入到表中,然后使用对新创建元素的非常量引用调用f1
。否则,使用对等效元素的引用调用f2
;只有当使用*_cvisit
重载时,该引用才是常量。
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果存在具有等效键的元素,则不会构造
如果发出重新哈希,则使指向元素的指针和引用无效。 该接口仅用于说明,因为C++不允许在可变参数包之后声明参数 只有当 |
insert_or_assign
template<class M> bool insert_or_assign(const key_type& k, M&& obj);
template<class M> bool insert_or_assign(key_type&& k, M&& obj);
template<class K, class M> bool insert_or_assign(K&& k, M&& obj);
将一个新元素插入到表中或通过赋值给包含的值来更新现有元素。
如果存在具有键 `k` 的元素,则通过赋值 `std::forward<M>(obj)` 来更新它。
如果没有这样的元素,则将其添加到表中,形式为
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 `template<class K, class M>` 只有在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义时才参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这使得异构查找能够避免实例化 `Key` 类型实例的成本。 |
erase
size_type erase(const key_type& k);
template<class K> size_type erase(const K& k);
如果存在,则删除键与k
等效的元素。
返回值 |
删除的元素数量(0或1)。 |
抛出 |
只有当 |
注释 |
只有当 |
按键删除
template<class F> size_type erase_if(const key_type& k, F f);
template<class K, class F> size_type erase_if(const K& k, F f);
如果存在,并且f(x)
为true
,则删除键与k
等效的元素x
。
返回值 |
删除的元素数量(0或1)。 |
抛出 |
只有当 |
注释 |
只有当 只有当 |
erase_if
template<class F> size_type erase_if(F f);
连续地使用对表中每个元素的引用调用f
,并删除f
返回true
的那些元素。
返回值 |
删除的元素数量。 |
抛出 |
只有当 |
并行 erase_if
template<class ExecutionPolicy, class F> void erase_if(ExecutionPolicy&& policy, F f);
使用对表中每个元素的引用调用f
,并删除f
返回true
的那些元素。根据指定的执行策略的语义并行执行。
抛出 |
根据所用执行策略的异常处理机制,如果在 |
注释 |
仅在支持C++17并行算法的编译器中可用。 只有当 不允许无序执行策略。 |
交换 (swap)
void swap(concurrent_flat_map& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_swap::value);
将表的内容与参数交换。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换表的分配器。否则,使用不相等的分配器进行交换会导致未定义的行为。
抛出 |
除非 |
并发性 |
阻塞于 `*this` 和 `other`。 |
清空 (clear)
void clear() noexcept;
删除表中的所有元素。
后置条件 |
|
并发性 |
阻塞于 `*this`。 |
合并 (merge)
template<class H2, class P2>
size_type merge(concurrent_flat_map<Key, T, H2, P2, Allocator>& source);
template<class H2, class P2>
size_type merge(concurrent_flat_map<Key, T, H2, P2, Allocator>&& source);
将 source
中所有键在 *this
中不存在的元素移动插入到 *this
中,并从 source
中删除这些元素。
返回值 |
插入的元素数量。 |
并发性 |
阻塞 |
映射操作
计数 (count)
size_type count(const key_type& k) const;
template<class K>
size_type count(const K& k) const;
返回值 |
键与 |
注释 |
只有当 在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。 |
包含 (contains)
bool contains(const key_type& k) const;
template<class K>
bool contains(const K& k) const;
返回值 |
一个布尔值,指示表中是否存在键等于 |
注释 |
只有当 在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。 |
哈希策略
负载因子 (load_factor)
float load_factor() const noexcept;
返回值 |
|
设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果 |
不执行任何操作,因为不允许用户更改此参数。为了与 |
最大负载
size_type max_load() const noexcept;
返回值 |
在不重新哈希的情况下,表可以容纳的最大元素数量,假设不会再删除任何元素。 |
注意 |
构造、重新哈希或清除后,表的最大负载至少为 在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。 |
重新哈希 (rehash)
void rehash(size_type n);
如有必要,更改桶数组的大小,使其至少有n
个桶,并且负载因子小于或等于最大负载因子。在适用情况下,这将扩大或缩小与表关联的bucket_count()
。
当size() == 0
时,rehash(0)
将释放底层桶数组。
使指向元素的指针和引用失效,并更改元素的顺序。
抛出 |
如果抛出异常,则该函数无效,除非异常是由表的哈希函数或比较函数抛出的。 |
并发性 |
阻塞于 `*this`。 |
预留空间 (reserve)
void reserve(size_type n);
等效于 a.rehash(ceil(n / a.max_load_factor()))
。
类似于rehash
,此函数可用于增加或减少表中桶的数量。
使指向元素的指针和引用失效,并更改元素的顺序。
抛出 |
如果抛出异常,则该函数无效,除非异常是由表的哈希函数或比较函数抛出的。 |
并发性 |
阻塞于 `*this`。 |
推导指南
如果出现以下任何情况,则推导指南不会参与重载解析:
-
它具有
InputIterator
模板参数,并且为该参数推导的类型不符合输入迭代器的资格。 -
它具有
Allocator
模板参数,并且为该参数推导的类型不符合分配器的资格。 -
它具有
Hash
模板参数,并且为该参数推导的类型为整数类型或符合分配器资格的类型。 -
它具有
Pred
模板参数,并且为该参数推导的类型符合分配器资格的类型。
推导指南中的size_type
参数类型指的是由推导指南推导出的表类型的size_type
成员类型。其默认值与所选构造函数的默认值一致。
迭代器值类型 (iter-value-type)
template<class InputIterator> using iter-value-type = typename std::iterator_traits<InputIterator>::value_type; // exposition only
迭代器键类型 (iter-key-type)
template<class InputIterator> using iter-key-type = std::remove_const_t< std::tuple_element_t<0, iter-value-type<InputIterator>>>; // exposition only
迭代器映射类型 (iter-mapped-type)
template<class InputIterator> using iter-mapped-type = std::tuple_element_t<1, iter-value-type<InputIterator>>; // exposition only
迭代器到分配器类型 (iter-to-alloc-type)
template<class InputIterator> using iter-to-alloc-type = std::pair< std::add_const_t<std::tuple_element_t<0, iter-value-type<InputIterator>>>, std::tuple_element_t<1, iter-value-type<InputIterator>>>; // exposition only
相等比较
相等运算符 (operator==)
template<class Key, class T, class Hash, class Pred, class Alloc>
bool operator==(const concurrent_flat_map<Key, T, Hash, Pred, Alloc>& x,
const concurrent_flat_map<Key, T, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个键相同、值相等(使用operator==
比较值类型)的元素,则返回true
。
并发性 |
阻塞 |
注释 |
如果两个表没有等效的相等谓词,则行为未定义。 |
不相等运算符 (operator!=)
template<class Key, class T, class Hash, class Pred, class Alloc>
bool operator!=(const concurrent_flat_map<Key, T, Hash, Pred, Alloc>& x,
const concurrent_flat_map<Key, T, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个键相同、值相等(使用operator==
比较值类型)的元素,则返回false
。
并发性 |
阻塞 |
注释 |
如果两个表没有等效的相等谓词,则行为未定义。 |
交换
template<class Key, class T, class Hash, class Pred, class Alloc>
void swap(concurrent_flat_map<Key, T, Hash, Pred, Alloc>& x,
concurrent_flat_map<Key, T, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
等价于
x.swap(y);
erase_if
template<class K, class T, class H, class P, class A, class Predicate>
typename concurrent_flat_map<K, T, H, P, A>::size_type
erase_if(concurrent_flat_map<K, T, H, P, A>& c, Predicate pred);
等价于
c.erase_if(pred);
序列化
可以使用Boost.Serialization提供的API通过存档/检索concurrent_flat_map
。支持常规存档和XML存档。
将concurrent_flat_map保存到存档
将concurrent_flat_map
x
的所有元素保存到存档(XML存档)ar
。
要求 |
|
并发性 |
阻塞 |
从存档加载concurrent_flat_map
删除concurrent_flat_map
x
中所有预先存在的元素,并从存档(XML存档)ar
插入原始concurrent_flat_map
other
(已保存到ar
读取的存储中)元素的已恢复副本。
要求 |
|
并发性 |
阻塞 |
类模板 concurrent_flat_set
boost::concurrent_flat_set
— 一个哈希表,它存储唯一值并允许并发元素插入、删除、查找和访问,而无需外部同步机制。
尽管它充当容器,但boost::concurrent_flat_set
并不符合标准C++ Container概念。特别是,没有提供迭代器和相关操作(begin
、end
等)。元素访问是通过用户提供的_访问函数_完成的,这些函数被传递到concurrent_flat_set
操作中,并在其中以受控方式内部执行。这种基于访问的API允许低争用并发使用场景。
boost::concurrent_flat_set
的内部数据结构类似于boost::unordered_flat_set
。由于它使用开放寻址技术,value_type
必须是可移动构造的,并且在重新哈希时不保持指针稳定性。
概要
// #include <boost/unordered/concurrent_flat_set.hpp> namespace boost { template<class Key, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<Key>> class concurrent_flat_set { public: // types using key_type = Key; using value_type = Key; using init_type = Key; using hasher = Hash; using key_equal = Pred; using allocator_type = Allocator; using pointer = typename std::allocator_traits<Allocator>::pointer; using const_pointer = typename std::allocator_traits<Allocator>::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 = stats-type; // if statistics are enabled // constants static constexpr size_type bulk_visit_size = implementation-defined; // construct/copy/destroy concurrent_flat_set(); explicit concurrent_flat_set(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); template<class InputIterator> 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()); concurrent_flat_set(const concurrent_flat_set& other); concurrent_flat_set(concurrent_flat_set&& other); template<class InputIterator> concurrent_flat_set(InputIterator f, InputIterator l,const allocator_type& a); explicit concurrent_flat_set(const Allocator& a); concurrent_flat_set(const concurrent_flat_set& other, const Allocator& a); concurrent_flat_set(concurrent_flat_set&& other, const Allocator& a); concurrent_flat_set(unordered_flat_set<Key, Hash, Pred, Allocator>&& other); concurrent_flat_set(std::initializer_list<value_type> il, size_type n = implementation-defined const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); concurrent_flat_set(size_type n, const allocator_type& a); concurrent_flat_set(size_type n, const hasher& hf, const allocator_type& a); template<class InputIterator> concurrent_flat_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a); template<class InputIterator> concurrent_flat_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a); concurrent_flat_set(std::initializer_list<value_type> il, const allocator_type& a); concurrent_flat_set(std::initializer_list<value_type> il, size_type n, const allocator_type& a); concurrent_flat_set(std::initializer_list<value_type> il, size_type n, const hasher& hf, const allocator_type& a); ~concurrent_flat_set(); concurrent_flat_set& operator=(const concurrent_flat_set& other); concurrent_flat_set& operator=(concurrent_flat_set&& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value); concurrent_flat_set& operator=(std::initializer_list<value_type>); allocator_type get_allocator() const noexcept; // visitation template<class F> size_t visit(const key_type& k, F f); template<class F> size_t visit(const key_type& k, F f) const; template<class F> size_t cvisit(const key_type& k, F f) const; template<class K, class F> size_t visit(const K& k, F f); template<class K, class F> size_t visit(const K& k, F f) const; template<class K, class F> size_t cvisit(const K& k, F f) const; template<class FwdIterator, class F> size_t visit(FwdIterator first, FwdIterator last, F f); template<class FwdIterator, class F> size_t visit(FwdIterator first, FwdIterator last, F f) const; template<class FwdIterator, class F> size_t cvisit(FwdIterator first, FwdIterator last, F f) const; template<class F> size_t visit_all(F f); template<class F> size_t visit_all(F f) const; template<class F> size_t cvisit_all(F f) const; template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f); template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f) const; template<class ExecutionPolicy, class F> void cvisit_all(ExecutionPolicy&& policy, F f) const; template<class F> bool visit_while(F f); template<class F> bool visit_while(F f) const; template<class F> bool cvisit_while(F f) const; template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f); template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f) const; template<class ExecutionPolicy, class F> bool cvisit_while(ExecutionPolicy&& policy, F f) const; // capacity [[nodiscard]] bool empty() const noexcept; size_type size() const noexcept; size_type max_size() const noexcept; // modifiers template<class... Args> bool emplace(Args&&... args); bool insert(const value_type& obj); bool insert(value_type&& obj); template<class K> bool insert(K&& k); template<class InputIterator> size_type insert(InputIterator first, InputIterator last); size_type insert(std::initializer_list<value_type> il); template<class... Args, class F> bool emplace_or_visit(Args&&... args, F&& f); template<class... Args, class F> bool emplace_or_cvisit(Args&&... args, F&& f); template<class F> bool insert_or_visit(const value_type& obj, F f); template<class F> bool insert_or_cvisit(const value_type& obj, F f); template<class F> bool insert_or_visit(value_type&& obj, F f); template<class F> bool insert_or_cvisit(value_type&& obj, F f); template<class K, class F> bool insert_or_visit(K&& k, F f); template<class K, class F> bool insert_or_cvisit(K&& k, F f); template<class InputIterator,class F> size_type insert_or_visit(InputIterator first, InputIterator last, F f); template<class InputIterator,class F> size_type insert_or_cvisit(InputIterator first, InputIterator last, F f); template<class F> size_type insert_or_visit(std::initializer_list<value_type> il, F f); template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> il, F f); template<class... Args, class F1, class F2> bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2); template<class... Args, class F1, class F2> bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2); template<class F1, class F2> bool insert_and_visit(const value_type& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_visit(value_type&& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2); template<class K, class F1, class F2> bool insert_and_visit(K&& k, F1 f1, F2 f2); template<class K, class F1, class F2> bool insert_and_cvisit(K&& k, F1 f1, F2 f2); template<class InputIterator,class F1, class F2> size_type insert_and_visit(InputIterator first, InputIterator last, F1 f1, F2 f2); template<class InputIterator,class F1, class F2> size_type insert_and_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2); template<class F1, class F2> size_type insert_and_visit(std::initializer_list<value_type> il, F1 f1, F2 f2); template<class F1, class F2> size_type insert_and_cvisit(std::initializer_list<value_type> il, F1 f1, F2 f2); size_type erase(const key_type& k); template<class K> size_type erase(const K& k); template<class F> size_type erase_if(const key_type& k, F f); template<class K, class F> size_type erase_if(const K& k, F f); template<class F> size_type erase_if(F f); template<class ExecutionPolicy, class F> void erase_if(ExecutionPolicy&& policy, F f); void swap(concurrent_flat_set& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || boost::allocator_traits<Allocator>::propagate_on_container_swap::value); void clear() noexcept; template<class H2, class P2> size_type merge(concurrent_flat_set<Key, H2, P2, Allocator>& source); template<class H2, class P2> size_type merge(concurrent_flat_set<Key, H2, P2, Allocator>&& source); // observers hasher hash_function() const; key_equal key_eq() const; // set operations size_type count(const key_type& k) const; template<class K> size_type count(const K& k) const; bool contains(const key_type& k) const; template<class K> bool contains(const K& k) const; // bucket interface size_type bucket_count() const noexcept; // hash policy float load_factor() const noexcept; float max_load_factor() const noexcept; void max_load_factor(float z); size_type max_load() const noexcept; void rehash(size_type n); void reserve(size_type n); // statistics (if enabled) stats get_stats() const; void reset_stats() noexcept; }; // Deduction Guides template<class InputIterator, class Hash = boost::hash<iter-value-type<InputIterator>>, class Pred = std::equal_to<iter-value-type<InputIterator>>, class Allocator = std::allocator<iter-value-type<InputIterator>>> concurrent_flat_set(InputIterator, InputIterator, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> concurrent_flat_set<iter-value-type<InputIterator>, Hash, Pred, Allocator>; template<class T, class Hash = boost::hash<T>, class Pred = std::equal_to<T>, class Allocator = std::allocator<T>> concurrent_flat_set(std::initializer_list<T>, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> concurrent_flat_set<T, Hash, Pred, Allocator>; template<class InputIterator, class Allocator> concurrent_flat_set(InputIterator, InputIterator, typename see below::size_type, Allocator) -> concurrent_flat_set<iter-value-type<InputIterator>, boost::hash<iter-value-type<InputIterator>>, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class InputIterator, class Allocator> concurrent_flat_set(InputIterator, InputIterator, Allocator) -> concurrent_flat_set<iter-value-type<InputIterator>, boost::hash<iter-value-type<InputIterator>>, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class InputIterator, class Hash, class Allocator> concurrent_flat_set(InputIterator, InputIterator, typename see below::size_type, Hash, Allocator) -> concurrent_flat_set<iter-value-type<InputIterator>, Hash, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class T, class Allocator> concurrent_flat_set(std::initializer_list<T>, typename see below::size_type, Allocator) -> concurrent_flat_set<T, boost::hash<T>, std::equal_to<T>, Allocator>; template<class T, class Allocator> concurrent_flat_set(std::initializer_list<T>, Allocator) -> concurrent_flat_set<T, boost::hash<T>, std::equal_to<T>, Allocator>; template<class T, class Hash, class Allocator> concurrent_flat_set(std::initializer_list<T>, typename see below::size_type, Hash, Allocator) -> concurrent_flat_set<T, Hash, std::equal_to<T>, Allocator>; // Equality Comparisons template<class Key, class Hash, class Pred, class Alloc> bool operator==(const concurrent_flat_set<Key, Hash, Pred, Alloc>& x, const concurrent_flat_set<Key, Hash, Pred, Alloc>& y); template<class Key, class Hash, class Pred, class Alloc> bool operator!=(const concurrent_flat_set<Key, Hash, Pred, Alloc>& x, const concurrent_flat_set<Key, Hash, Pred, Alloc>& y); // swap template<class Key, class Hash, class Pred, class Alloc> void swap(concurrent_flat_set<Key, Hash, Pred, Alloc>& x, concurrent_flat_set<Key, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y))); // Erasure template<class K, class H, class P, class A, class Predicate> typename concurrent_flat_set<K, H, P, A>::size_type erase_if(concurrent_flat_set<K, H, P, A>& c, Predicate pred); // Pmr aliases (C++17 and up) namespace unordered::pmr { template<class Key, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>> using concurrent_flat_set = boost::concurrent_flat_set<Key, Hash, Pred, std::pmr::polymorphic_allocator<Key>>; } }
描述
模板参数
键 |
|
哈希 |
一个一元函数对象类型,充当 |
Pred |
一个二元函数对象,它在 |
分配器 |
其值类型与表的值类型相同的分配器。 |
表的元素保存在内部的桶数组中。元素插入到由其哈希码确定的桶中,但如果该桶已被占用(发生冲突),则使用原始位置附近的一个可用桶。
桶数组的大小可以通过调用 `insert`/`emplace` 自动增加,或者通过调用 `rehash`/`reserve` 来增加。表的负载因子(元素数量除以桶的数量)永远不会大于 `max_load_factor()`,除非在较小的尺寸下,实现可能决定允许更高的负载。
如果hash_is_avalanching<Hash>::value
为true
,则哈希函数按原样使用;否则,会添加一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。
并发需求和保证
对同一 `Hash` 或 `Pred` 常量实例的并发调用 `operator()` 不会引入数据竞争。对于 `Alloc` 是 `Allocator` 或从 `Allocator` 重新绑定的任何分配器类型,对同一 `Alloc` 实例 `al` 的以下操作的并发调用不会引入数据竞争。
-
从 `al` 复制构造从 `Alloc` 重新绑定的分配器
-
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` 的非 const 容器函数中,针对最后一个(或唯一一个)访问函数。
-
任何插入或修改元素e
的boost::concurrent_flat_set
操作都与对e
的访问函数的内部调用同步。
由boost::concurrent_flat_set
x
执行的访问函数不允许调用x
上的任何操作;仅当y
上并发未完成的操作不会直接或间接访问x
时,才允许调用不同的boost::concurrent_flat_set
实例y
上的操作。
配置宏
BOOST_UNORDERED_DISABLE_REENTRANCY_CHECK
在调试版本中(更准确地说,当 BOOST_ASSERT_IS_VOID
未定义时),会检测到容器重入(从访问 `m` 元素的函数中非法调用 `m` 上的操作)并通过 `BOOST_ASSERT_MSG` 发出信号。当运行时速度成为关注点时,可以通过全局定义此宏来禁用此功能。
构造函数
默认构造函数
concurrent_flat_set();
使用 `hasher()` 作为哈希函数、`key_equal()` 作为键相等谓词和 `allocator_type()` 作为分配器构造一个空表。
后置条件 |
|
要求 |
如果使用默认值,则 |
桶计数构造函数
explicit concurrent_flat_set(size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用 `hf` 作为哈希函数、`eql` 作为键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。
后置条件 |
|
要求 |
如果使用默认值,则 |
迭代器范围构造函数
template<class InputIterator>
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());
使用 `hf` 作为哈希函数、`eql` 作为键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表,并将元素从 `[f, l)` 插入到其中。
要求 |
如果使用默认值,则 |
复制构造函数
concurrent_flat_set(concurrent_flat_set const& other);
复制构造函数。复制包含的元素、哈希函数、谓词和分配器。
如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。
要求 |
`value_type` 可复制构造。 |
并发性 |
阻塞 |
移动构造函数
concurrent_flat_set(concurrent_flat_set&& other);
移动构造函数。other
的内部桶数组直接转移到新表。哈希函数、谓词和分配器是从other
移动构造的。如果启用了统计信息,则将内部统计信息从other
转移并调用other.reset_stats()
。
并发性 |
阻塞 |
带分配器的迭代器范围构造函数
template<class InputIterator>
concurrent_flat_set(InputIterator f, InputIterator l, const allocator_type& a);
使用 `a` 作为分配器,使用默认哈希函数和键相等谓词构造一个空表,并将元素从 `[f, l)` 插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带分配器的复制构造函数
concurrent_flat_set(concurrent_flat_set const& other, Allocator const& a);
构造一个表,复制 `other` 的包含元素、哈希函数和谓词,但使用分配器 `a`。
并发性 |
阻塞 |
带分配器的移动构造函数
concurrent_flat_set(concurrent_flat_set&& other, Allocator const& a);
如果a == other.get_allocator()
,则other
的元素将直接转移到新表;否则,将从other
的元素移动构造元素。哈希函数和谓词是从other
移动构造的,分配器是从a
复制构造的。如果启用了统计信息,则当且仅当a == other.get_allocator()
时才从other
转移内部统计信息,并且始终调用other.reset_stats()
。
并发性 |
阻塞 |
从unordered_flat_set的移动构造函数
concurrent_flat_set(unordered_flat_set<Key, Hash, Pred, Allocator>&& other);
从unordered_flat_set
移动构造。other
的内部桶数组直接转移到新的容器。哈希函数、谓词和分配器是从other
移动构造的。如果启用了统计信息,则将内部统计信息从other
转移并调用other.reset_stats()
。
复杂度 |
O( |
初始化列表构造函数
concurrent_flat_set(std::initializer_list<value_type> il,
size_type n = implementation-defined
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用 `hf` 作为哈希函数、`eql` 作为键相等谓词和 `a` 构造一个至少具有 `n` 个桶的空表,并将元素从 `il` 插入到其中。
要求 |
如果使用默认值,则 |
带分配器的桶计数构造函数
concurrent_flat_set(size_type n, allocator_type const& a);
使用 `hf` 作为哈希函数、默认哈希函数和键相等谓词以及 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。
后置条件 |
|
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带哈希函数和分配器的桶计数构造函数
concurrent_flat_set(size_type n, hasher const& hf, allocator_type const& a);
使用 `hf` 作为哈希函数、默认键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。
后置条件 |
|
要求 |
`key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的迭代器范围构造函数
template<class InputIterator>
concurrent_flat_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a);
使用 `a` 作为分配器和默认哈希函数和键相等谓词构造一个至少具有 `n` 个桶的空表,并将元素从 `[f, l)` 插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和哈希函数的迭代器范围构造函数
template<class InputIterator>
concurrent_flat_set(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a);
使用 `hf` 作为哈希函数、`a` 作为分配器、默认键相等谓词构造一个至少具有 `n` 个桶的空表,并将元素从 `[f, l)` 插入到其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
带分配器的初始化列表构造函数
concurrent_flat_set(std::initializer_list<value_type> il, const allocator_type& a);
使用 `a` 和默认哈希函数和键相等谓词构造一个空表,并将元素从 `il` 插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的初始化列表构造函数
concurrent_flat_set(std::initializer_list<value_type> il, size_type n, const allocator_type& a);
使用 `a` 和默认哈希函数和键相等谓词构造一个至少具有 `n` 个桶的空表,并将元素从 `il` 插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数、哈希函数和分配器的初始化列表构造函数
concurrent_flat_set(std::initializer_list<value_type> il, size_type n, const hasher& hf,
const allocator_type& a);
使用 `hf` 作为哈希函数、`a` 作为分配器和默认键相等谓词构造一个至少具有 `n` 个桶的空表,并将元素从 `il` 插入到其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
赋值
复制赋值
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
元素的副本。
要求 |
|
并发性 |
阻塞于 `*this` 和 `other`。 |
移动赋值
concurrent_flat_set& operator=(concurrent_flat_set&& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::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
元素的移动构造副本。如果启用了统计信息,则当且仅当最终分配器等于other.get_allocator()
时才从other
转移内部统计信息,并且始终调用other.reset_stats()
。
并发性 |
阻塞于 `*this` 和 `other`。 |
访问
[c]visit
template<class F> size_t visit(const key_type& k, F f);
template<class F> size_t visit(const key_type& k, F f) const;
template<class F> size_t cvisit(const key_type& k, F f) const;
template<class K, class F> size_t visit(const K& k, F f);
template<class K, class F> size_t visit(const K& k, F f) const;
template<class K, class F> size_t cvisit(const K& k, F f) const;
如果存在键与k
等效的元素x
,则使用对x
的常量引用调用f
。
返回值 |
访问的元素数量(0 或 1)。 |
注释 |
只有当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义时,`template<class K, class F>` 重载才会参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这允许异构查找,避免了实例化 `Key` 类型实例的成本。 |
批量访问
template<class FwdIterator, class F>
size_t visit(FwdIterator first, FwdIterator last, F f);
template<class FwdIterator, class F>
size_t visit(FwdIterator first, FwdIterator last, F f) const;
template<class FwdIterator, class F>
size_t cvisit(FwdIterator first, FwdIterator last, F f) const;
对于范围[first
, last
)中的每个元素k
,如果容器中存在键与k
等效的元素x
,则使用对x
的常量引用调用f
。
尽管在功能上等同于分别对每个键调用[c]visit
,但由于内部简化优化,批量访问通常执行速度更快。建议std::distance(first,last)
至少为bulk_visit_size
以获得性能提升:超过此大小,性能预计不会进一步提高。
要求 |
|
返回值 |
已访问元素的数量。 |
[c]visit_all
template<class F> size_t visit_all(F f);
template<class F> size_t visit_all(F f) const;
template<class F> size_t cvisit_all(F f) const;
连续使用对表中每个元素的常量引用调用f
。
返回值 |
已访问元素的数量。 |
并行 [c]visit_all
template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f);
template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f) const;
template<class ExecutionPolicy, class F> void cvisit_all(ExecutionPolicy&& policy, F f) const;
使用对表中每个元素的常量引用调用f
。根据指定的执行策略的语义并行执行。
抛出 |
根据所用执行策略的异常处理机制,如果在 |
注释 |
仅在支持C++17并行算法的编译器中可用。 只有当 不允许无序执行策略。 |
[c]visit_while
template<class F> bool visit_while(F f);
template<class F> bool visit_while(F f) const;
template<class F> bool cvisit_while(F f) const;
连续使用对表中每个元素的常量引用调用f
,直到f
返回false
或访问所有元素。
返回值 |
如果 |
并行 [c]visit_while
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f);
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f) const;
template<class ExecutionPolicy, class F> bool cvisit_while(ExecutionPolicy&& policy, F f) const;
使用对表中每个元素的常量引用调用f
,直到f
返回false
或访问所有元素。根据指定的执行策略的语义并行执行。
返回值 |
如果 |
抛出 |
根据所用执行策略的异常处理机制,如果在 |
注释 |
仅在支持C++17并行算法的编译器中可用。 只有当 不允许无序执行策略。 并行化意味着执行并不一定在 |
修改器
emplace
template<class... Args> bool emplace(Args&&... args);
仅当表中不存在具有等效键的元素时,才使用参数args
构造的对象插入到表中。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 |
复制插入
bool insert(const value_type& obj);
仅当表中不存在具有等效键的元素时,才将obj
插入到表中。
要求 |
`value_type` 可 复制插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 |
移动插入
bool insert(value_type&& obj);
仅当表中不存在具有等效键的元素时,才将obj
插入到表中。
要求 |
`value_type` 可 移动插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 |
透明插入
template<class K> bool insert(K&& k);
当且仅当容器中没有具有等效键的元素时,才将从std::forward<K>(k)
构造的元素插入到容器中。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 只有当 |
插入迭代器范围
template<class InputIterator> size_type insert(InputIterator first, InputIterator last);
等价于
while(first != last) this->emplace(*first++);
返回值 |
插入的元素数量。 |
插入初始化列表
size_type insert(std::initializer_list<value_type> il);
等价于
this->insert(il.begin(), il.end());
返回值 |
插入的元素数量。 |
emplace_or_[c]visit
template<class... Args, class F> bool emplace_or_visit(Args&&... args, F&& f);
template<class... Args, class F> bool emplace_or_cvisit(Args&&... args, F&& f);
如果表中没有键等效的元素,则使用参数args
构造的对象插入到表中。否则,使用对等效元素的常量引用调用f
。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 该接口仅用于说明,因为C++不允许在可变参数包之后声明参数 |
复制 insert_or_[c]visit
template<class F> bool insert_or_visit(const value_type& obj, F f);
template<class F> bool insert_or_cvisit(const value_type& obj, F f);
当且仅当表中没有键等效的元素时,才将obj
插入到表中。否则,使用对等效元素的常量引用调用f
。
要求 |
`value_type` 可 复制插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 |
移动 insert_or_[c]visit
template<class F> bool insert_or_visit(value_type&& obj, F f);
template<class F> bool insert_or_cvisit(value_type&& obj, F f);
当且仅当表中没有键等效的元素时,才将obj
插入到表中。否则,使用对等效元素的常量引用调用f
。
要求 |
`value_type` 可 移动插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 |
透明insert_or_[c]visit
template<class K, class F> bool insert_or_visit(K&& k, F f);
template<class K, class F> bool insert_or_cvisit(K&& k, F f);
当且仅当容器中没有键等效的元素时,才将从std::forward<K>(k)
构造的元素插入到容器中。否则,使用对等效元素的常量引用调用f
。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 只有当 |
插入迭代器范围或访问
template<class InputIterator,class F>
size_type insert_or_visit(InputIterator first, InputIterator last, F f);
template<class InputIterator,class F>
size_type insert_or_cvisit(InputIterator first, InputIterator last, F f);
等价于
while(first != last) this->emplace_or_[c]visit(*first++, f);
返回值 |
插入的元素数量。 |
插入初始化列表或访问
template<class F> size_type insert_or_visit(std::initializer_list<value_type> il, F f);
template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> il, F f);
等价于
this->insert_or_[c]visit(il.begin(), il.end(), std::ref(f));
返回值 |
插入的元素数量。 |
emplace_and_[c]visit
template<class... Args, class F1, class F2>
bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2);
如果表中没有键等效的元素,则使用参数args
构造的对象插入到表中,然后使用对新创建元素的常量引用调用f1
。否则,使用对等效元素的常量引用调用f2
。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 该接口仅用于说明,因为C++不允许在可变参数包之后声明参数 |
复制 insert_and_[c]visit
template<class F1, class F2> bool insert_and_visit(const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2);
当且仅当表中没有键等效的元素时,才将obj
插入到表中,然后使用对新创建元素的常量引用调用f1
。否则,使用对等效元素的常量引用调用f2
。
要求 |
`value_type` 可 复制插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 |
移动 insert_and_[c]visit
template<class F1, class F2> bool insert_and_visit(value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2);
当且仅当表中没有键等效的元素时,才将obj
插入到表中,然后使用对新创建元素的常量引用调用f1
。否则,使用对等效元素的常量引用调用f2
。
要求 |
`value_type` 可 移动插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 |
透明insert_and_[c]visit
template<class K, class F1, class F2> bool insert_and_visit(K&& k, F1 f1, F2 f2);
template<class K, class F1, class F2> bool insert_and_cvisit(K&& k, F1 f1, F2 f2);
当且仅当容器中没有键等效的元素时,才将从std::forward<K>(k)
构造的元素插入到容器中,然后使用对新创建元素的常量引用调用f1
。否则,使用对等效元素的常量引用调用f2
。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果发出重新哈希,则使指向元素的指针和引用无效。 只有当 |
插入迭代器范围和访问
template<class InputIterator,class F1, class F2>
size_type insert_and_visit(InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class InputIterator,class F1, class F2>
size_type insert_and_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2);
等价于
while(first != last) this->emplace_and_[c]visit(*first++, f1, f2);
返回值 |
插入的元素数量。 |
插入初始化列表和访问
template<class F1, class F2>
size_type insert_and_visit(std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
size_type insert_and_cvisit(std::initializer_list<value_type> il, F1 f1, F2 f2);
等价于
this->insert_and_[c]visit(il.begin(), il.end(), std::ref(f1), std::ref(f2));
返回值 |
插入的元素数量。 |
erase
size_type erase(const key_type& k);
template<class K> size_type erase(const K& k);
如果存在,则删除键与k
等效的元素。
返回值 |
删除的元素数量(0或1)。 |
抛出 |
只有当 |
注释 |
只有当 |
按键删除
template<class F> size_type erase_if(const key_type& k, F f);
template<class K, class F> size_type erase_if(const K& k, F f);
如果存在,并且f(x)
为true
,则删除键与k
等效的元素x
。
返回值 |
删除的元素数量(0或1)。 |
抛出 |
只有当 |
注释 |
只有当 只有当 |
erase_if
template<class F> size_type erase_if(F f);
连续地使用对表中每个元素的引用调用f
,并删除f
返回true
的那些元素。
返回值 |
删除的元素数量。 |
抛出 |
只有当 |
并行 erase_if
template<class ExecutionPolicy, class F> void erase_if(ExecutionPolicy&& policy, F f);
使用对表中每个元素的引用调用f
,并删除f
返回true
的那些元素。根据指定的执行策略的语义并行执行。
抛出 |
根据所用执行策略的异常处理机制,如果在 |
注释 |
仅在支持C++17并行算法的编译器中可用。 只有当 不允许无序执行策略。 |
交换 (swap)
void swap(concurrent_flat_set& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_swap::value);
将表的内容与参数交换。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换表的分配器。否则,使用不相等的分配器进行交换会导致未定义的行为。
抛出 |
除非 |
并发性 |
阻塞于 `*this` 和 `other`。 |
清空 (clear)
void clear() noexcept;
删除表中的所有元素。
后置条件 |
|
并发性 |
阻塞于 `*this`。 |
合并 (merge)
template<class H2, class P2>
size_type merge(concurrent_flat_set<Key, H2, P2, Allocator>& source);
template<class H2, class P2>
size_type merge(concurrent_flat_set<Key, H2, P2, Allocator>&& source);
将 source
中所有键在 *this
中不存在的元素移动插入到 *this
中,并从 source
中删除这些元素。
返回值 |
插入的元素数量。 |
并发性 |
阻塞 |
集合操作
计数 (count)
size_type count(const key_type& k) const;
template<class K>
size_type count(const K& k) const;
返回值 |
键与 |
注释 |
只有当 在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。 |
包含 (contains)
bool contains(const key_type& k) const;
template<class K>
bool contains(const K& k) const;
返回值 |
一个布尔值,指示表中是否存在键等于 |
注释 |
只有当 在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。 |
哈希策略
负载因子 (load_factor)
float load_factor() const noexcept;
返回值 |
|
设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果 |
不执行任何操作,因为不允许用户更改此参数。为了与 |
最大负载
size_type max_load() const noexcept;
返回值 |
在不重新哈希的情况下,表可以容纳的最大元素数量,假设不会再删除任何元素。 |
注意 |
构造、重新哈希或清除后,表的最大负载至少为 在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。 |
重新哈希 (rehash)
void rehash(size_type n);
如有必要,更改桶数组的大小,使其至少有n
个桶,并且负载因子小于或等于最大负载因子。在适用情况下,这将扩大或缩小与表关联的bucket_count()
。
当size() == 0
时,rehash(0)
将释放底层桶数组。
使指向元素的指针和引用失效,并更改元素的顺序。
抛出 |
如果抛出异常,则该函数无效,除非异常是由表的哈希函数或比较函数抛出的。 |
并发性 |
阻塞于 `*this`。 |
预留空间 (reserve)
void reserve(size_type n);
等效于 a.rehash(ceil(n / a.max_load_factor()))
。
类似于rehash
,此函数可用于增加或减少表中桶的数量。
使指向元素的指针和引用失效,并更改元素的顺序。
抛出 |
如果抛出异常,则该函数无效,除非异常是由表的哈希函数或比较函数抛出的。 |
并发性 |
阻塞于 `*this`。 |
推导指南
如果出现以下任何情况,则推导指南不会参与重载解析:
-
它具有
InputIterator
模板参数,并且为该参数推导的类型不符合输入迭代器的资格。 -
它具有
Allocator
模板参数,并且为该参数推导的类型不符合分配器的资格。 -
它具有
Hash
模板参数,并且为该参数推导的类型为整数类型或符合分配器资格的类型。 -
它具有
Pred
模板参数,并且为该参数推导的类型符合分配器资格的类型。
推导指南中的size_type
参数类型引用由推导指南推导出的容器类型的size_type
成员类型。其默认值与所选构造函数的默认值一致。
迭代器值类型 (iter-value-type)
template<class InputIterator> using iter-value-type = typename std::iterator_traits<InputIterator>::value_type; // exposition only
相等比较
相等运算符 (operator==)
template<class Key, class Hash, class Pred, class Alloc>
bool operator==(const concurrent_flat_set<Key, Hash, Pred, Alloc>& x,
const concurrent_flat_set<Key, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个键相同、值相等(使用operator==
比较值类型)的元素,则返回true
。
并发性 |
阻塞 |
注释 |
如果两个表没有等效的相等谓词,则行为未定义。 |
不相等运算符 (operator!=)
template<class Key, class Hash, class Pred, class Alloc>
bool operator!=(const concurrent_flat_set<Key, Hash, Pred, Alloc>& x,
const concurrent_flat_set<Key, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个键相同、值相等(使用operator==
比较值类型)的元素,则返回false
。
并发性 |
阻塞 |
注释 |
如果两个表没有等效的相等谓词,则行为未定义。 |
交换
template<class Key, class Hash, class Pred, class Alloc>
void swap(concurrent_flat_set<Key, Hash, Pred, Alloc>& x,
concurrent_flat_set<Key, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
等价于
x.swap(y);
erase_if
template<class K, class H, class P, class A, class Predicate>
typename concurrent_flat_set<K, H, P, A>::size_type
erase_if(concurrent_flat_set<K, H, P, A>& c, Predicate pred);
等价于
c.erase_if(pred);
序列化
可以通过此库提供的API使用Boost.Serialization存档/检索concurrent_flat_set
。支持常规存档和XML存档。
将concurrent_flat_set保存到存档
将concurrent_flat_set
x
的所有元素保存到存档(XML存档)ar
中。
要求 |
|
并发性 |
阻塞 |
从存档加载concurrent_flat_set
删除concurrent_flat_set
x
中所有预先存在的元素,并从存档(XML存档)ar
中插入原始concurrent_flat_set
other
元素的已恢复副本(这些元素已保存到ar
读取的存储区中)。
要求 |
|
并发性 |
阻塞 |
类模板 concurrent_node_map
boost::concurrent_node_map
— 一个基于节点的哈希表,它将唯一键与另一个值关联起来,并允许并发插入、删除、查找和访问元素,无需外部同步机制。
尽管它充当容器,但boost::concurrent_node_map
并不符合标准C++ Container概念。特别是,不提供迭代器和相关操作(begin
、end
等)。元素访问和修改是通过用户提供的_访问函数_来完成的,这些函数传递给concurrent_node_map
操作,并在内部以受控方式执行。这种基于访问的API允许低争用并发使用场景。
boost::concurrent_node_map
的内部数据结构类似于boost::unordered_node_map
。与boost::concurrent_flat_map
不同,它提供了指针稳定性和节点处理功能,但代价可能是性能较低。
概要
// #include <boost/unordered/concurrent_node_map.hpp> namespace boost { template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T>>> class concurrent_node_map { public: // types using key_type = Key; using mapped_type = T; using value_type = std::pair<const Key, T>; using init_type = std::pair< typename std::remove_const<Key>::type, typename std::remove_const<T>::type >; using hasher = Hash; using key_equal = Pred; using allocator_type = Allocator; using pointer = typename std::allocator_traits<Allocator>::pointer; using const_pointer = typename std::allocator_traits<Allocator>::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 = stats-type; // if statistics are enabled // constants static constexpr size_type bulk_visit_size = implementation-defined; // construct/copy/destroy concurrent_node_map(); explicit concurrent_node_map(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); template<class InputIterator> 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()); concurrent_node_map(const concurrent_node_map& other); concurrent_node_map(concurrent_node_map&& other); template<class InputIterator> concurrent_node_map(InputIterator f, InputIterator l,const allocator_type& a); explicit concurrent_node_map(const Allocator& a); concurrent_node_map(const concurrent_node_map& other, const Allocator& a); concurrent_node_map(concurrent_node_map&& other, const Allocator& a); concurrent_node_map(unordered_node_map<Key, T, Hash, Pred, Allocator>&& other); concurrent_node_map(std::initializer_list<value_type> il, size_type n = implementation-defined const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); concurrent_node_map(size_type n, const allocator_type& a); concurrent_node_map(size_type n, const hasher& hf, const allocator_type& a); template<class InputIterator> concurrent_node_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a); template<class InputIterator> concurrent_node_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a); concurrent_node_map(std::initializer_list<value_type> il, const allocator_type& a); concurrent_node_map(std::initializer_list<value_type> il, size_type n, const allocator_type& a); concurrent_node_map(std::initializer_list<value_type> il, size_type n, const hasher& hf, const allocator_type& a); ~concurrent_node_map(); concurrent_node_map& operator=(const concurrent_node_map& other); concurrent_node_map& operator=(concurrent_node_map&& other) noexcept( (boost::allocator_traits<Allocator>::is_always_equal::value || boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) && std::is_same<pointer, value_type*>::value); concurrent_node_map& operator=(std::initializer_list<value_type>); allocator_type get_allocator() const noexcept; // visitation template<class F> size_t visit(const key_type& k, F f); template<class F> size_t visit(const key_type& k, F f) const; template<class F> size_t cvisit(const key_type& k, F f) const; template<class K, class F> size_t visit(const K& k, F f); template<class K, class F> size_t visit(const K& k, F f) const; template<class K, class F> size_t cvisit(const K& k, F f) const; template<class FwdIterator, class F> size_t visit(FwdIterator first, FwdIterator last, F f); template<class FwdIterator, class F> size_t visit(FwdIterator first, FwdIterator last, F f) const; template<class FwdIterator, class F> size_t cvisit(FwdIterator first, FwdIterator last, F f) const; template<class F> size_t visit_all(F f); template<class F> size_t visit_all(F f) const; template<class F> size_t cvisit_all(F f) const; template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f); template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f) const; template<class ExecutionPolicy, class F> void cvisit_all(ExecutionPolicy&& policy, F f) const; template<class F> bool visit_while(F f); template<class F> bool visit_while(F f) const; template<class F> bool cvisit_while(F f) const; template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f); template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f) const; template<class ExecutionPolicy, class F> bool cvisit_while(ExecutionPolicy&& policy, F f) const; // capacity [[nodiscard]] bool empty() const noexcept; size_type size() const noexcept; size_type max_size() const noexcept; // modifiers template<class... Args> bool emplace(Args&&... args); bool insert(const value_type& obj); bool insert(const init_type& obj); bool insert(value_type&& obj); bool insert(init_type&& obj); template<class InputIterator> size_type insert(InputIterator first, InputIterator last); size_type insert(std::initializer_list<value_type> il); insert_return_type insert(node_type&& nh); template<class... Args, class F> bool emplace_or_visit(Args&&... args, F&& f); template<class... Args, class F> bool emplace_or_cvisit(Args&&... args, F&& f); template<class F> bool insert_or_visit(const value_type& obj, F f); template<class F> bool insert_or_cvisit(const value_type& obj, F f); template<class F> bool insert_or_visit(const init_type& obj, F f); template<class F> bool insert_or_cvisit(const init_type& obj, F f); template<class F> bool insert_or_visit(value_type&& obj, F f); template<class F> bool insert_or_cvisit(value_type&& obj, F f); template<class F> bool insert_or_visit(init_type&& obj, F f); template<class F> bool insert_or_cvisit(init_type&& obj, F f); template<class InputIterator,class F> size_type insert_or_visit(InputIterator first, InputIterator last, F f); template<class InputIterator,class F> size_type insert_or_cvisit(InputIterator first, InputIterator last, F f); template<class F> size_type insert_or_visit(std::initializer_list<value_type> il, F f); template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> il, F f); template<class F> insert_return_type insert_or_visit(node_type&& nh, F f); template<class F> insert_return_type insert_or_cvisit(node_type&& nh, F f); template<class... Args, class F1, class F2> bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2); template<class... Args, class F1, class F2> bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2); template<class F1, class F2> bool insert_and_visit(const value_type& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_visit(const init_type& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_cvisit(const init_type& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_visit(value_type&& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_visit(init_type&& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2); template<class InputIterator,class F1, class F2> size_type insert_and_visit(InputIterator first, InputIterator last, F1 f1, F2 f2); template<class InputIterator,class F1, class F2> size_type insert_and_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2); template<class F1, class F2> size_type insert_and_visit(std::initializer_list<value_type> il, F1 f1, F2 f2); template<class F1, class F2> size_type insert_and_cvisit(std::initializer_list<value_type> il, F1 f1, F2 f2); template<class F1, class F2> insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2); template<class F1, class F2> insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2); template<class... Args> bool try_emplace(const key_type& k, Args&&... args); template<class... Args> bool try_emplace(key_type&& k, Args&&... args); template<class K, class... Args> bool try_emplace(K&& k, Args&&... args); template<class... Args, class F> bool try_emplace_or_visit(const key_type& k, Args&&... args, F&& f); template<class... Args, class F> bool try_emplace_or_cvisit(const key_type& k, Args&&... args, F&& f); template<class... Args, class F> bool try_emplace_or_visit(key_type&& k, Args&&... args, F&& f); template<class... Args, class F> bool try_emplace_or_cvisit(key_type&& k, Args&&... args, F&& f); template<class K, class... Args, class F> bool try_emplace_or_visit(K&& k, Args&&... args, F&& f); template<class K, class... Args, class F> bool try_emplace_or_cvisit(K&& k, Args&&... args, F&& f); template<class... Args, class F1, class F2> bool try_emplace_and_visit(const key_type& k, Args&&... args, F1&& f1, F2&& f2); template<class... Args, class F1, class F2> bool try_emplace_and_cvisit(const key_type& k, Args&&... args, F1&& f1, F2&& f2); template<class... Args, class F1, class F2> bool try_emplace_and_visit(key_type&& k, Args&&... args, F1&& f1, F2&& f2); template<class... Args, class F1, class F2> bool try_emplace_and_cvisit(key_type&& k, Args&&... args, F1&& f1, F2&& f2); template<class K, class... Args, class F1, class F2> bool try_emplace_and_visit(K&& k, Args&&... args, F1&& f1, F2&& f2); template<class K, class... Args, class F1, class F2> bool try_emplace_and_cvisit(K&& k, Args&&... args, F1&& f1, F2&& f2); template<class M> bool insert_or_assign(const key_type& k, M&& obj); template<class M> bool insert_or_assign(key_type&& k, M&& obj); template<class K, class M> bool insert_or_assign(K&& k, M&& obj); size_type erase(const key_type& k); template<class K> size_type erase(const K& k); template<class F> size_type erase_if(const key_type& k, F f); template<class K, class F> size_type erase_if(const K& k, F f); template<class F> size_type erase_if(F f); template<class ExecutionPolicy, class F> void erase_if(ExecutionPolicy&& policy, F f); void swap(concurrent_node_map& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || boost::allocator_traits<Allocator>::propagate_on_container_swap::value); node_type extract(const key_type& k); template<class K> node_type extract(const K& k); template<class F> node_type extract_if(const key_type& k, F f); template<class K, class F> node_type extract_if(const K& k, F f); void clear() noexcept; template<class H2, class P2> size_type merge(concurrent_node_map<Key, T, H2, P2, Allocator>& source); template<class H2, class P2> size_type merge(concurrent_node_map<Key, T, H2, P2, Allocator>&& source); // observers hasher hash_function() const; key_equal key_eq() const; // map operations size_type count(const key_type& k) const; template<class K> size_type count(const K& k) const; bool contains(const key_type& k) const; template<class K> bool contains(const K& k) const; // bucket interface size_type bucket_count() const noexcept; // hash policy float load_factor() const noexcept; float max_load_factor() const noexcept; void max_load_factor(float z); size_type max_load() const noexcept; void rehash(size_type n); void reserve(size_type n); // statistics (if enabled) stats get_stats() const; void reset_stats() noexcept; }; // Deduction Guides template<class InputIterator, class Hash = boost::hash<iter-key-type<InputIterator>>, class Pred = std::equal_to<iter-key-type<InputIterator>>, class Allocator = std::allocator<iter-to-alloc-type<InputIterator>>> concurrent_node_map(InputIterator, InputIterator, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> concurrent_node_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, Hash, Pred, Allocator>; template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T>>> concurrent_node_map(std::initializer_list<std::pair<Key, T>>, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> concurrent_node_map<Key, T, Hash, Pred, Allocator>; template<class InputIterator, class Allocator> concurrent_node_map(InputIterator, InputIterator, typename see below::size_type, Allocator) -> concurrent_node_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, boost::hash<iter-key-type<InputIterator>>, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class InputIterator, class Allocator> concurrent_node_map(InputIterator, InputIterator, Allocator) -> concurrent_node_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, boost::hash<iter-key-type<InputIterator>>, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class InputIterator, class Hash, class Allocator> concurrent_node_map(InputIterator, InputIterator, typename see below::size_type, Hash, Allocator) -> concurrent_node_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, Hash, std::equal_to<iter-key-type<InputIterator>>, Allocator>; template<class Key, class T, class Allocator> concurrent_node_map(std::initializer_list<std::pair<Key, T>>, typename see below::size_type, Allocator) -> concurrent_node_map<Key, T, boost::hash<Key>, std::equal_to<Key>, Allocator>; template<class Key, class T, class Allocator> concurrent_node_map(std::initializer_list<std::pair<Key, T>>, Allocator) -> concurrent_node_map<Key, T, boost::hash<Key>, std::equal_to<Key>, Allocator>; template<class Key, class T, class Hash, class Allocator> concurrent_node_map(std::initializer_list<std::pair<Key, T>>, typename see below::size_type, Hash, Allocator) -> concurrent_node_map<Key, T, Hash, std::equal_to<Key>, Allocator>; // Equality Comparisons template<class Key, class T, class Hash, class Pred, class Alloc> bool operator==(const concurrent_node_map<Key, T, Hash, Pred, Alloc>& x, const concurrent_node_map<Key, T, Hash, Pred, Alloc>& y); template<class Key, class T, class Hash, class Pred, class Alloc> bool operator!=(const concurrent_node_map<Key, T, Hash, Pred, Alloc>& x, const concurrent_node_map<Key, T, Hash, Pred, Alloc>& y); // swap template<class Key, class T, class Hash, class Pred, class Alloc> void swap(concurrent_node_map<Key, T, Hash, Pred, Alloc>& x, concurrent_node_map<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y))); // Erasure template<class K, class T, class H, class P, class A, class Predicate> typename concurrent_node_map<K, T, H, P, A>::size_type erase_if(concurrent_node_map<K, T, H, P, A>& c, Predicate pred); // Pmr aliases (C++17 and up) namespace unordered::pmr { template<class Key, class T, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>> using concurrent_node_map = boost::concurrent_node_map<Key, T, Hash, Pred, std::pmr::polymorphic_allocator<std::pair<const Key, T>>>; } }
描述
模板参数
键 |
必须能够从任何可转换为它的 |
T |
|
哈希 |
一个一元函数对象类型,充当 |
Pred |
一个二元函数对象,它在 |
分配器 |
一个值类型与表的值类型相同的分配器。支持使用 花哨指针 的分配器。 |
表的元素节点保存在内部的_桶数组_中。节点插入到由其元素哈希码确定的桶中,但如果桶已被占用(_冲突_),则使用原始位置附近的一个可用桶。
桶数组的大小可以通过调用 `insert`/`emplace` 自动增加,或者通过调用 `rehash`/`reserve` 来增加。表的负载因子(元素数量除以桶的数量)永远不会大于 `max_load_factor()`,除非在较小的尺寸下,实现可能决定允许更高的负载。
如果hash_is_avalanching<Hash>::value
为true
,则哈希函数按原样使用;否则,会添加一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。
并发需求和保证
对同一 `Hash` 或 `Pred` 常量实例的并发调用 `operator()` 不会引入数据竞争。对于 `Alloc` 是 `Allocator` 或从 `Allocator` 重新绑定的任何分配器类型,对同一 `Alloc` 实例 `al` 的以下操作的并发调用不会引入数据竞争。
-
从 `al` 复制构造从 `Alloc` 重新绑定的分配器
-
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` 的非 const 容器函数中,针对最后一个(或唯一一个)访问函数。
-
任何插入或修改元素e
的boost::concurrent_node_map
操作都将与访问函数对e
的内部调用同步。
boost::concurrent_node_map
x
执行的访问函数不允许调用x
上的任何操作;只有在y
上并发未完成的操作不会直接或间接访问x
时,才允许调用不同的boost::concurrent_node_map
实例y
上的操作。
配置宏
BOOST_UNORDERED_DISABLE_REENTRANCY_CHECK
在调试版本中(更准确地说,当 BOOST_ASSERT_IS_VOID
未定义时),会检测到容器重入(从访问 `m` 元素的函数中非法调用 `m` 上的操作)并通过 `BOOST_ASSERT_MSG` 发出信号。当运行时速度成为关注点时,可以通过全局定义此宏来禁用此功能。
类型定义
typedef implementation-defined node_type;
一个用于保存提取的表元素的类,模拟NodeHandle。
typedef implementation-defined insert_return_type;
内部类模板的特化
template<class NodeType>
struct insert_return_type // name is exposition only
{
bool inserted;
NodeType node;
};
其中NodeType
= node_type
。
构造函数
默认构造函数
concurrent_node_map();
使用 `hasher()` 作为哈希函数、`key_equal()` 作为键相等谓词和 `allocator_type()` 作为分配器构造一个空表。
后置条件 |
|
要求 |
如果使用默认值,则 |
桶计数构造函数
explicit concurrent_node_map(size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用 `hf` 作为哈希函数、`eql` 作为键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。
后置条件 |
|
要求 |
如果使用默认值,则 |
迭代器范围构造函数
template<class InputIterator>
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());
使用 `hf` 作为哈希函数、`eql` 作为键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表,并将元素从 `[f, l)` 插入到其中。
要求 |
如果使用默认值,则 |
复制构造函数
concurrent_node_map(concurrent_node_map const& other);
复制构造函数。复制包含的元素、哈希函数、谓词和分配器。
如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。
要求 |
`value_type` 可复制构造。 |
并发性 |
阻塞 |
移动构造函数
concurrent_node_map(concurrent_node_map&& other);
移动构造函数。other
的内部桶数组直接转移到新表。哈希函数、谓词和分配器是从other
移动构造的。如果已启用统计信息,则将内部统计信息从other
转移并调用other.reset_stats()
。
并发性 |
阻塞 |
带分配器的迭代器范围构造函数
template<class InputIterator>
concurrent_node_map(InputIterator f, InputIterator l, const allocator_type& a);
使用 `a` 作为分配器,使用默认哈希函数和键相等谓词构造一个空表,并将元素从 `[f, l)` 插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带分配器的复制构造函数
concurrent_node_map(concurrent_node_map const& other, Allocator const& a);
构造一个表,复制 `other` 的包含元素、哈希函数和谓词,但使用分配器 `a`。
并发性 |
阻塞 |
带分配器的移动构造函数
concurrent_node_map(concurrent_node_map&& other, Allocator const& a);
如果a == other.get_allocator()
,则other
的元素将直接转移到新表;否则,将从other
的元素移动构造元素。哈希函数和谓词是从other
移动构造的,分配器是从a
复制构造的。如果已启用统计信息,则当且仅当a == other.get_allocator()
时,才将内部统计信息从other
转移,并且始终调用other.reset_stats()
。
并发性 |
阻塞 |
从unordered_node_map的移动构造函数
concurrent_node_map(unordered_node_map<Key, T, Hash, Pred, Allocator>&& other);
从unordered_node_map
移动构造。other
的内部桶数组直接转移到新的容器。哈希函数、谓词和分配器是从other
移动构造的。如果已启用统计信息,则将内部统计信息从other
转移并调用other.reset_stats()
。
复杂度 |
O( |
初始化列表构造函数
concurrent_node_map(std::initializer_list<value_type> il,
size_type n = implementation-defined
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用 `hf` 作为哈希函数、`eql` 作为键相等谓词和 `a` 构造一个至少具有 `n` 个桶的空表,并将元素从 `il` 插入到其中。
要求 |
如果使用默认值,则 |
带分配器的桶计数构造函数
concurrent_node_map(size_type n, allocator_type const& a);
使用 `hf` 作为哈希函数、默认哈希函数和键相等谓词以及 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。
后置条件 |
|
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带哈希函数和分配器的桶计数构造函数
concurrent_node_map(size_type n, hasher const& hf, allocator_type const& a);
使用 `hf` 作为哈希函数、默认键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。
后置条件 |
|
要求 |
`key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的迭代器范围构造函数
template<class InputIterator>
concurrent_node_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a);
使用 `a` 作为分配器和默认哈希函数和键相等谓词构造一个至少具有 `n` 个桶的空表,并将元素从 `[f, l)` 插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和哈希函数的迭代器范围构造函数
template<class InputIterator>
concurrent_node_map(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a);
使用 `hf` 作为哈希函数、`a` 作为分配器、默认键相等谓词构造一个至少具有 `n` 个桶的空表,并将元素从 `[f, l)` 插入到其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
带分配器的初始化列表构造函数
concurrent_node_map(std::initializer_list<value_type> il, const allocator_type& a);
使用 `a` 和默认哈希函数和键相等谓词构造一个空表,并将元素从 `il` 插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的初始化列表构造函数
concurrent_node_map(std::initializer_list<value_type> il, size_type n, const allocator_type& a);
使用 `a` 和默认哈希函数和键相等谓词构造一个至少具有 `n` 个桶的空表,并将元素从 `il` 插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数、哈希函数和分配器的初始化列表构造函数
concurrent_node_map(std::initializer_list<value_type> il, size_type n, const hasher& hf,
const allocator_type& a);
使用 `hf` 作为哈希函数、`a` 作为分配器和默认键相等谓词构造一个至少具有 `n` 个桶的空表,并将元素从 `il` 插入到其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
赋值
复制赋值
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
元素的副本。
要求 |
|
并发性 |
阻塞于 `*this` 和 `other`。 |
移动赋值
concurrent_node_map& operator=(concurrent_node_map&& other)
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
std::is_same<pointer, value_type*>::value);
移动赋值运算符。销毁先前存在的元素,交换other
的哈希函数和谓词,如果存在Alloc::propagate_on_container_move_assignment
并且Alloc::propagate_on_container_move_assignment::value
为true
,则从other
移动赋值分配器。如果此时分配器等于other.get_allocator()
,则other
的内部桶数组将直接转移到*this
;否则,插入other
元素的移动构造副本。如果已启用统计信息,则当且仅当最终分配器等于other.get_allocator()
时,才将内部统计信息从other
转移,并且始终调用other.reset_stats()
。
并发性 |
阻塞于 `*this` 和 `other`。 |
访问
[c]visit
template<class F> size_t visit(const key_type& k, F f);
template<class F> size_t visit(const key_type& k, F f) const;
template<class F> size_t cvisit(const key_type& k, F f) const;
template<class K, class F> size_t visit(const K& k, F f);
template<class K, class F> size_t visit(const K& k, F f) const;
template<class K, class F> size_t cvisit(const K& k, F f) const;
如果存在键与 `k` 等效的元素 `x`,则使用对 `x` 的引用调用 `f`。当且仅当 `*this` 为 const 时,此引用才是 const。
返回值 |
访问的元素数量(0 或 1)。 |
注释 |
只有当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义时,`template<class K, class F>` 重载才会参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这允许异构查找,避免了实例化 `Key` 类型实例的成本。 |
批量访问
template<class FwdIterator, class F>
size_t visit(FwdIterator first, FwdIterator last, F f);
template<class FwdIterator, class F>
size_t visit(FwdIterator first, FwdIterator last, F f) const;
template<class FwdIterator, class F>
size_t cvisit(FwdIterator first, FwdIterator last, F f) const;
对于范围 [first
, last
) 中的每个元素 `k`,如果容器中存在键与 `k` 等效的元素 `x`,则使用对 `x` 的引用调用 `f`。当且仅当 `*this` 为 const 时,此引用才是 const。
尽管在功能上等效于对每个键分别调用[c]visit
,但由于内部简化优化,批量访问通常执行速度更快。建议std::distance(first,last)
至少为bulk_visit_size
才能获得性能提升:超过此大小后,性能预计不会进一步提高。
要求 |
|
返回值 |
已访问元素的数量。 |
[c]visit_all
template<class F> size_t visit_all(F f);
template<class F> size_t visit_all(F f) const;
template<class F> size_t cvisit_all(F f) const;
连续地使用对表中每个元素的引用调用f
。只有当*this
是常量时,这些引用才是常量。
返回值 |
已访问元素的数量。 |
并行 [c]visit_all
template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f);
template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f) const;
template<class ExecutionPolicy, class F> void cvisit_all(ExecutionPolicy&& policy, F f) const;
使用对表中每个元素的引用调用f
。只有当*this
是常量时,这些引用才是常量。根据指定的执行策略的语义并行执行。
抛出 |
根据所用执行策略的异常处理机制,如果在 |
注释 |
仅在支持C++17并行算法的编译器中可用。 只有当 不允许无序执行策略。 |
[c]visit_while
template<class F> bool visit_while(F f);
template<class F> bool visit_while(F f) const;
template<class F> bool cvisit_while(F f) const;
连续地使用对表中每个元素的引用调用f
,直到f
返回false
或访问所有元素。只有当*this
是常量时,这些元素引用才是常量。
返回值 |
如果 |
并行 [c]visit_while
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f);
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f) const;
template<class ExecutionPolicy, class F> bool cvisit_while(ExecutionPolicy&& policy, F f) const;
调用f
,使用对表中每个元素的引用,直到f
返回false
或访问所有元素。只有当*this
是常量时,这些元素引用才是常量。根据指定的执行策略的语义并行执行。
返回值 |
如果 |
抛出 |
根据所用执行策略的异常处理机制,如果在 |
注释 |
仅在支持C++17并行算法的编译器中可用。 只有当 不允许无序执行策略。 并行化意味着执行并不一定在 |
修改器
emplace
template<class... Args> bool emplace(Args&&... args);
仅当表中不存在具有等效键的元素时,才使用参数args
构造的对象插入到表中。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果 |
复制插入
bool insert(const value_type& obj);
bool insert(const init_type& obj);
仅当表中不存在具有等效键的元素时,才将obj
插入到表中。
要求 |
`value_type` 可 复制插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
当 |
移动插入
bool insert(value_type&& obj);
bool insert(init_type&& obj);
仅当表中不存在具有等效键的元素时,才将obj
插入到表中。
要求 |
`value_type` 可 移动插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
当 |
插入迭代器范围
template<class InputIterator> size_type insert(InputIterator first, InputIterator last);
等价于
while(first != last) this->emplace(*first++);
返回值 |
插入的元素数量。 |
插入初始化列表
size_type insert(std::initializer_list<value_type> il);
等价于
this->insert(il.begin(), il.end());
返回值 |
插入的元素数量。 |
插入节点
insert_return_type insert(node_type&& nh);
如果nh
不为空,当且仅当表中没有与nh.key()
等效的键的元素时,才将关联元素插入表中。函数返回时,nh
为空。
返回值 |
由
|
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
并发性 |
阻塞 |
注释 |
如果 |
emplace_or_[c]visit
template<class... Args, class F> bool emplace_or_visit(Args&&... args, F&& f);
template<class... Args, class F> bool emplace_or_cvisit(Args&&... args, F&& f);
如果表中不存在具有等效键的元素,则使用参数args
构造的对象插入到表中。否则,使用对等效元素的引用调用f
;如果使用的是emplace_or_cvisit
,则该引用为常量。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
该接口仅用于说明,因为C++不允许在可变参数包之后声明参数 |
复制 insert_or_[c]visit
template<class F> bool insert_or_visit(const value_type& obj, F f);
template<class F> bool insert_or_cvisit(const value_type& obj, F f);
template<class F> bool insert_or_visit(const init_type& obj, F f);
template<class F> bool insert_or_cvisit(const init_type& obj, F f);
仅当表中不存在具有等效键的元素时,才将obj
插入到表中。否则,使用对等效元素的引用调用f
;只有当使用*_cvisit
重载时,该引用才是常量。
要求 |
`value_type` 可 复制插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
在 |
移动 insert_or_[c]visit
template<class F> bool insert_or_visit(value_type&& obj, F f);
template<class F> bool insert_or_cvisit(value_type&& obj, F f);
template<class F> bool insert_or_visit(init_type&& obj, F f);
template<class F> bool insert_or_cvisit(init_type&& obj, F f);
仅当表中不存在具有等效键的元素时,才将obj
插入到表中。否则,使用对等效元素的引用调用f
;只有当使用*_cvisit
重载时,该引用才是常量。
要求 |
`value_type` 可 移动插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
在 |
插入迭代器范围或访问
template<class InputIterator,class F>
size_type insert_or_visit(InputIterator first, InputIterator last, F f);
template<class InputIterator,class F>
size_type insert_or_cvisit(InputIterator first, InputIterator last, F f);
等价于
while(first != last) this->emplace_or_[c]visit(*first++, f);
返回值 |
插入的元素数量。 |
插入初始化列表或访问
template<class F> size_type insert_or_visit(std::initializer_list<value_type> il, F f);
template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> il, F f);
等价于
this->insert_or_[c]visit(il.begin(), il.end(), std::ref(f));
返回值 |
插入的元素数量。 |
插入节点或访问
template<class F> insert_return_type insert_or_visit(node_type&& nh, F f);
template<class F> insert_return_type insert_or_cvisit(node_type&& nh, F f);
如果nh
为空,则不执行任何操作。否则,当且仅当表中没有与nh.key()
等效的键的元素时,才将关联元素插入表中。否则,使用对等效元素的引用调用f
;此引用当且仅当使用insert_or_cvisit
时为const。
返回值 |
由
|
抛出 |
如果操作(而不是对 |
并发性 |
阻塞 |
注释 |
如果 |
emplace_and_[c]visit
template<class... Args, class F1, class F2>
bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2);
如果表中不存在具有等效键的元素,则使用参数args
构造的对象插入到表中,然后使用对新创建元素的非常量引用调用f1
。否则,使用对等效元素的引用调用f2
;如果使用的是emplace_and_cvisit
,则该引用为常量。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
该接口仅用于说明,因为C++不允许在可变参数包之后声明参数 |
复制 insert_and_[c]visit
template<class F1, class F2> bool insert_and_visit(const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_visit(const init_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(const init_type& obj, F1 f1, F2 f2);
仅当表中不存在具有等效键的元素时,才将obj
插入到表中,然后使用对新创建元素的非常量引用调用f1
。否则,使用对等效元素的引用调用f2
;只有当使用*_cvisit
重载时,该引用才是常量。
要求 |
`value_type` 可 复制插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
在 |
移动 insert_and_[c]visit
template<class F1, class F2> bool insert_and_visit(value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_visit(init_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2);
仅当表中不存在具有等效键的元素时,才将obj
插入到表中,然后使用对新创建元素的非常量引用调用f1
。否则,使用对等效元素的引用调用f2
;只有当使用*_cvisit
重载时,该引用才是常量。
要求 |
`value_type` 可 移动插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
在 |
插入迭代器范围和访问
template<class InputIterator, class F1, class F2>
size_type insert_or_visit(InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class InputIterator, class F1, class F2>
size_type insert_or_cvisit(InputIterator first, InputIterator last, F1 f2, F2 f2);
等价于
while(first != last) this->emplace_and_[c]visit(*first++, f1, f2);
返回值 |
插入的元素数量。 |
插入初始化列表和访问
template<class F1, class F2>
size_type insert_and_visit(std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
size_type insert_and_cvisit(std::initializer_list<value_type> il, F1 f1, F2 f2);
等价于
this->insert_and_[c]visit(il.begin(), il.end(), std::ref(f1), std::ref(f2));
返回值 |
插入的元素数量。 |
插入节点并访问
template<class F1, class F2>
insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2);
template<class F1, class F2>
insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2);
如果nh
为空,则不执行任何操作。否则,当且仅当表中没有与nh.key()
等效的键的元素时,才将关联元素插入表中,然后使用对新插入元素的非const引用调用f1
。否则,使用对等效元素的引用调用f2
;此引用当且仅当使用insert_or_cvisit
时为const。
返回值 |
由
|
抛出 |
如果操作(而不是对 |
并发性 |
阻塞 |
注释 |
如果 |
try_emplace
template<class... Args> bool try_emplace(const key_type& k, Args&&... args);
template<class... Args> bool try_emplace(key_type&& k, Args&&... args);
template<class K, class... Args> bool try_emplace(K&& k, Args&&... args);
如果表中不存在具有键k
的现有元素,则将使用k
和args
构造的元素插入到表中。
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
此函数类似于emplace,不同之处在于,如果存在具有等效键的元素,则不构造
与emplace不同,后者只是将所有参数转发到 只有当 |
try_emplace_or_[c]visit
template<class... Args, class F>
bool try_emplace_or_visit(const key_type& k, Args&&... args, F&& f);
template<class... Args, class F>
bool try_emplace_or_cvisit(const key_type& k, Args&&... args, F&& f);
template<class... Args, class F>
bool try_emplace_or_visit(key_type&& k, Args&&... args, F&& f);
template<class... Args, class F>
bool try_emplace_or_cvisit(key_type&& k, Args&&... args, F&& f);
template<class K, class... Args, class F>
bool try_emplace_or_visit(K&& k, Args&&... args, F&& f);
template<class K, class... Args, class F>
bool try_emplace_or_cvisit(K&& k, Args&&... args, F&& f);
如果表中不存在具有键k
的现有元素,则将使用k
和args
构造的元素插入到表中。否则,使用对等效元素的引用调用f
;只有当使用*_cvisit
重载时,该引用才是常量。
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果存在具有等效键的元素,则不会构造
该接口仅用于说明,因为C++不允许在可变参数包之后声明参数 只有当 |
try_emplace_and_[c]visit
template<class... Args, class F1, class F2>
bool try_emplace_and_visit(const key_type& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool try_emplace_and_cvisit(const key_type& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool try_emplace_and_visit(key_type&& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool try_emplace_and_cvisit(key_type&& k, Args&&... args, F1&& f1, F2&& f2);
template<class K, class... Args, class F1, class F2>
bool try_emplace_and_visit(K&& k, Args&&... args, F1&& f1, F2&& f2);
template<class K, class... Args, class F1, class F2>
bool try_emplace_and_cvisit(K&& k, Args&&... args, F1&& f1, F2&& f2);
如果表中不存在具有键k
的现有元素,则将使用k
和args
构造的元素插入到表中,然后使用对新创建元素的非常量引用调用f1
。否则,使用对等效元素的引用调用f2
;只有当使用*_cvisit
重载时,该引用才是常量。
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
如果存在具有等效键的元素,则不会构造
该接口仅供说明,因为C++不允许在可变参数包之后声明参数 只有当 |
insert_or_assign
template<class M> bool insert_or_assign(const key_type& k, M&& obj);
template<class M> bool insert_or_assign(key_type&& k, M&& obj);
template<class K, class M> bool insert_or_assign(K&& k, M&& obj);
将一个新元素插入到表中或通过赋值给包含的值来更新现有元素。
如果存在具有键 `k` 的元素,则通过赋值 `std::forward<M>(obj)` 来更新它。
如果没有这样的元素,则将其添加到表中,形式为
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
`template<class K, class M>` 只有在 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义时才参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这使得异构查找能够避免实例化 `Key` 类型实例的成本。 |
erase
size_type erase(const key_type& k);
template<class K> size_type erase(const K& k);
如果存在,则删除键与k
等效的元素。
返回值 |
删除的元素数量(0或1)。 |
抛出 |
只有当 |
注释 |
只有当 |
按键删除
template<class F> size_type erase_if(const key_type& k, F f);
template<class K, class F> size_type erase_if(const K& k, F f);
如果存在,并且f(x)
为true
,则删除键与k
等效的元素x
。
返回值 |
删除的元素数量(0或1)。 |
抛出 |
只有当 |
注释 |
只有当 只有当 |
erase_if
template<class F> size_type erase_if(F f);
连续地使用对表中每个元素的引用调用f
,并删除f
返回true
的那些元素。
返回值 |
删除的元素数量。 |
抛出 |
只有当 |
并行 erase_if
template<class ExecutionPolicy, class F> void erase_if(ExecutionPolicy&& policy, F f);
使用对表中每个元素的引用调用f
,并删除f
返回true
的那些元素。根据指定的执行策略的语义并行执行。
抛出 |
根据所用执行策略的异常处理机制,如果在 |
注释 |
仅在支持C++17并行算法的编译器中可用。 只有当 不允许无序执行策略。 |
交换 (swap)
void swap(concurrent_node_map& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_swap::value);
将表的内容与参数交换。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换表的分配器。否则,使用不相等的分配器进行交换会导致未定义的行为。
抛出 |
除非 |
并发性 |
阻塞于 `*this` 和 `other`。 |
提取
node_type extract(const key_type& k);
template<class K> node_type extract(K&& k);
提取键与 k
等效的元素(如果存在)。
返回值 |
包含提取元素的 |
抛出 |
只有当 |
注释 |
只有当 |
条件提取
template<class F> node_type extract_if(const key_type& k, F f);
template<class K, class F> node_type extract_if(K&& k, F f);
如果存在元素x
且其键等效于k
,并且f(x)
为true
,则提取该元素。
返回值 |
包含提取元素的 |
抛出 |
仅当 |
注释 |
只有当 |
清空 (clear)
void clear() noexcept;
删除表中的所有元素。
后置条件 |
|
并发性 |
阻塞于 `*this`。 |
合并 (merge)
template<class H2, class P2>
size_type merge(concurrent_node_map<Key, T, H2, P2, Allocator>& source);
template<class H2, class P2>
size_type merge(concurrent_node_map<Key, T, H2, P2, Allocator>&& source);
将 source
中所有键在 *this
中不存在的元素移动插入到 *this
中,并从 source
中删除这些元素。
返回值 |
插入的元素数量。 |
并发性 |
阻塞 |
映射操作
计数 (count)
size_type count(const key_type& k) const;
template<class K>
size_type count(const K& k) const;
返回值 |
键与 |
注释 |
只有当 在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。 |
包含 (contains)
bool contains(const key_type& k) const;
template<class K>
bool contains(const K& k) const;
返回值 |
一个布尔值,指示表中是否存在键等于 |
注释 |
只有当 在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。 |
哈希策略
负载因子 (load_factor)
float load_factor() const noexcept;
返回值 |
|
设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果 |
不执行任何操作,因为不允许用户更改此参数。为了与 |
最大负载
size_type max_load() const noexcept;
返回值 |
在不重新哈希的情况下,表可以容纳的最大元素数量,假设不会再删除任何元素。 |
注意 |
构造、重新哈希或清除后,表的最大负载至少为 在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。 |
重新哈希 (rehash)
void rehash(size_type n);
如有必要,更改桶数组的大小,使其至少有n
个桶,并且负载因子小于或等于最大负载因子。在适用情况下,这将扩大或缩小与表关联的bucket_count()
。
当size() == 0
时,rehash(0)
将释放底层桶数组。
抛出 |
如果抛出异常,则该函数无效,除非异常是由表的哈希函数或比较函数抛出的。 |
并发性 |
阻塞于 `*this`。 |
预留空间 (reserve)
void reserve(size_type n);
等效于 a.rehash(ceil(n / a.max_load_factor()))
。
类似于rehash
,此函数可用于增加或减少表中桶的数量。
抛出 |
如果抛出异常,则该函数无效,除非异常是由表的哈希函数或比较函数抛出的。 |
并发性 |
阻塞于 `*this`。 |
推导指南
如果出现以下任何情况,则推导指南不会参与重载解析:
-
它具有
InputIterator
模板参数,并且为该参数推导的类型不符合输入迭代器的资格。 -
它具有
Allocator
模板参数,并且为该参数推导的类型不符合分配器的资格。 -
它具有
Hash
模板参数,并且为该参数推导的类型为整数类型或符合分配器资格的类型。 -
它具有
Pred
模板参数,并且为该参数推导的类型符合分配器资格的类型。
推导指南中的size_type
参数类型指的是由推导指南推导出的表类型的size_type
成员类型。其默认值与所选构造函数的默认值一致。
迭代器值类型 (iter-value-type)
template<class InputIterator> using iter-value-type = typename std::iterator_traits<InputIterator>::value_type; // exposition only
迭代器键类型 (iter-key-type)
template<class InputIterator> using iter-key-type = std::remove_const_t< std::tuple_element_t<0, iter-value-type<InputIterator>>>; // exposition only
迭代器映射类型 (iter-mapped-type)
template<class InputIterator> using iter-mapped-type = std::tuple_element_t<1, iter-value-type<InputIterator>>; // exposition only
迭代器到分配器类型 (iter-to-alloc-type)
template<class InputIterator> using iter-to-alloc-type = std::pair< std::add_const_t<std::tuple_element_t<0, iter-value-type<InputIterator>>>, std::tuple_element_t<1, iter-value-type<InputIterator>>>; // exposition only
相等比较
相等运算符 (operator==)
template<class Key, class T, class Hash, class Pred, class Alloc>
bool operator==(const concurrent_node_map<Key, T, Hash, Pred, Alloc>& x,
const concurrent_node_map<Key, T, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个键相同、值相等(使用operator==
比较值类型)的元素,则返回true
。
并发性 |
阻塞 |
注释 |
如果两个表没有等效的相等谓词,则行为未定义。 |
不相等运算符 (operator!=)
template<class Key, class T, class Hash, class Pred, class Alloc>
bool operator!=(const concurrent_node_map<Key, T, Hash, Pred, Alloc>& x,
const concurrent_node_map<Key, T, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个键相同、值相等(使用operator==
比较值类型)的元素,则返回false
。
并发性 |
阻塞 |
注释 |
如果两个表没有等效的相等谓词,则行为未定义。 |
交换
template<class Key, class T, class Hash, class Pred, class Alloc>
void swap(concurrent_node_map<Key, T, Hash, Pred, Alloc>& x,
concurrent_node_map<Key, T, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
等价于
x.swap(y);
erase_if
template<class K, class T, class H, class P, class A, class Predicate>
typename concurrent_node_map<K, T, H, P, A>::size_type
erase_if(concurrent_node_map<K, T, H, P, A>& c, Predicate pred);
等价于
c.erase_if(pred);
序列化
可以通过此库提供的API使用Boost.Serialization存档/检索concurrent_node_map
。支持常规存档和XML存档。
将concurrent_node_map保存到存档
将concurrent_node_map
x
的所有元素保存到存档(XML存档)ar
中。
要求 |
|
并发性 |
阻塞 |
从存档加载concurrent_node_map
删除concurrent_node_map
x
中所有预先存在的元素,并从存档(XML存档)ar
中插入原始concurrent_node_map
other
元素的已恢复副本(这些元素已保存到ar
读取的存储区中)。
要求 |
|
并发性 |
阻塞 |
类模板 concurrent_node_set
boost::concurrent_node_set
— 一个基于节点的哈希表,它存储唯一值,并允许并发插入、删除、查找和访问元素,无需外部同步机制。
尽管它充当容器,但boost::concurrent_node_set
并不符合标准C++ Container概念。特别是,不提供迭代器和相关操作(begin
、end
等)。元素访问是通过用户提供的_访问函数_来完成的,这些函数传递给concurrent_node_set
操作,并在内部以受控方式执行。这种基于访问的API允许低争用并发使用场景。
boost::concurrent_node_set
的内部数据结构类似于boost::unordered_node_set
。与boost::concurrent_flat_set
不同,它提供了指针稳定性和节点处理功能,但代价可能是性能较低。
概要
// #include <boost/unordered/concurrent_node_set.hpp> namespace boost { template<class Key, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<Key>> class concurrent_node_set { public: // types using key_type = Key; using value_type = Key; using init_type = Key; using hasher = Hash; using key_equal = Pred; using allocator_type = Allocator; using pointer = typename std::allocator_traits<Allocator>::pointer; using const_pointer = typename std::allocator_traits<Allocator>::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 = stats-type; // if statistics are enabled // constants static constexpr size_type bulk_visit_size = implementation-defined; // construct/copy/destroy concurrent_node_set(); explicit concurrent_node_set(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); template<class InputIterator> 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()); concurrent_node_set(const concurrent_node_set& other); concurrent_node_set(concurrent_node_set&& other); template<class InputIterator> concurrent_node_set(InputIterator f, InputIterator l,const allocator_type& a); explicit concurrent_node_set(const Allocator& a); concurrent_node_set(const concurrent_node_set& other, const Allocator& a); concurrent_node_set(concurrent_node_set&& other, const Allocator& a); concurrent_node_set(unordered_node_set<Key, Hash, Pred, Allocator>&& other); concurrent_node_set(std::initializer_list<value_type> il, size_type n = implementation-defined const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); concurrent_node_set(size_type n, const allocator_type& a); concurrent_node_set(size_type n, const hasher& hf, const allocator_type& a); template<class InputIterator> concurrent_node_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a); template<class InputIterator> concurrent_node_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a); concurrent_node_set(std::initializer_list<value_type> il, const allocator_type& a); concurrent_node_set(std::initializer_list<value_type> il, size_type n, const allocator_type& a); concurrent_node_set(std::initializer_list<value_type> il, size_type n, const hasher& hf, const allocator_type& a); ~concurrent_node_set(); concurrent_node_set& operator=(const concurrent_node_set& other); concurrent_node_set& operator=(concurrent_node_set&& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value); concurrent_node_set& operator=(std::initializer_list<value_type>); allocator_type get_allocator() const noexcept; // visitation template<class F> size_t visit(const key_type& k, F f); template<class F> size_t visit(const key_type& k, F f) const; template<class F> size_t cvisit(const key_type& k, F f) const; template<class K, class F> size_t visit(const K& k, F f); template<class K, class F> size_t visit(const K& k, F f) const; template<class K, class F> size_t cvisit(const K& k, F f) const; template<class FwdIterator, class F> size_t visit(FwdIterator first, FwdIterator last, F f); template<class FwdIterator, class F> size_t visit(FwdIterator first, FwdIterator last, F f) const; template<class FwdIterator, class F> size_t cvisit(FwdIterator first, FwdIterator last, F f) const; template<class F> size_t visit_all(F f); template<class F> size_t visit_all(F f) const; template<class F> size_t cvisit_all(F f) const; template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f); template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f) const; template<class ExecutionPolicy, class F> void cvisit_all(ExecutionPolicy&& policy, F f) const; template<class F> bool visit_while(F f); template<class F> bool visit_while(F f) const; template<class F> bool cvisit_while(F f) const; template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f); template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f) const; template<class ExecutionPolicy, class F> bool cvisit_while(ExecutionPolicy&& policy, F f) const; // capacity [[nodiscard]] bool empty() const noexcept; size_type size() const noexcept; size_type max_size() const noexcept; // modifiers template<class... Args> bool emplace(Args&&... args); bool insert(const value_type& obj); bool insert(value_type&& obj); template<class K> bool insert(K&& k); template<class InputIterator> size_type insert(InputIterator first, InputIterator last); size_type insert(std::initializer_list<value_type> il); insert_return_type insert(node_type&& nh); template<class... Args, class F> bool emplace_or_visit(Args&&... args, F&& f); template<class... Args, class F> bool emplace_or_cvisit(Args&&... args, F&& f); template<class F> bool insert_or_visit(const value_type& obj, F f); template<class F> bool insert_or_cvisit(const value_type& obj, F f); template<class F> bool insert_or_visit(value_type&& obj, F f); template<class F> bool insert_or_cvisit(value_type&& obj, F f); template<class K, class F> bool insert_or_visit(K&& k, F f); template<class K, class F> bool insert_or_cvisit(K&& k, F f); template<class InputIterator,class F> size_type insert_or_visit(InputIterator first, InputIterator last, F f); template<class InputIterator,class F> size_type insert_or_cvisit(InputIterator first, InputIterator last, F f); template<class F> size_type insert_or_visit(std::initializer_list<value_type> il, F f); template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> il, F f); template<class F> insert_return_type insert_or_visit(node_type&& nh, F f); template<class F> insert_return_type insert_or_cvisit(node_type&& nh, F f); template<class... Args, class F1, class F2> bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2); template<class... Args, class F1, class F2> bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2); template<class F1, class F2> bool insert_and_visit(const value_type& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_visit(value_type&& obj, F1 f1, F2 f2); template<class F1, class F2> bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2); template<class K, class F1, class F2> bool insert_and_visit(K&& k, F1 f1, F2 f2); template<class K, class F1, class F2> bool insert_and_cvisit(K&& k, F1 f1, F2 f2); template<class InputIterator,class F1, class F2> size_type insert_and_visit(InputIterator first, InputIterator last, F1 f1, F2 f2); template<class InputIterator,class F1, class F2> size_type insert_and_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2); template<class F1, class F2> size_type insert_and_visit(std::initializer_list<value_type> il, F1 f1, F2 f2); template<class F1, class F2> size_type insert_and_cvisit(std::initializer_list<value_type> il, F1 f1, F2 f2); template<class F1, class F2> insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2); template<class F1, class F2> insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2); size_type erase(const key_type& k); template<class K> size_type erase(const K& k); template<class F> size_type erase_if(const key_type& k, F f); template<class K, class F> size_type erase_if(const K& k, F f); template<class F> size_type erase_if(F f); template<class ExecutionPolicy, class F> void erase_if(ExecutionPolicy&& policy, F f); void swap(concurrent_node_set& other) noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || boost::allocator_traits<Allocator>::propagate_on_container_swap::value); node_type extract(const key_type& k); template<class K> node_type extract(const K& k); template<class F> node_type extract_if(const key_type& k, F f); template<class K, class F> node_type extract_if(const K& k, F f); void clear() noexcept; template<class H2, class P2> size_type merge(concurrent_node_set<Key, H2, P2, Allocator>& source); template<class H2, class P2> size_type merge(concurrent_node_set<Key, H2, P2, Allocator>&& source); // observers hasher hash_function() const; key_equal key_eq() const; // set operations size_type count(const key_type& k) const; template<class K> size_type count(const K& k) const; bool contains(const key_type& k) const; template<class K> bool contains(const K& k) const; // bucket interface size_type bucket_count() const noexcept; // hash policy float load_factor() const noexcept; float max_load_factor() const noexcept; void max_load_factor(float z); size_type max_load() const noexcept; void rehash(size_type n); void reserve(size_type n); // statistics (if enabled) stats get_stats() const; void reset_stats() noexcept; }; // Deduction Guides template<class InputIterator, class Hash = boost::hash<iter-value-type<InputIterator>>, class Pred = std::equal_to<iter-value-type<InputIterator>>, class Allocator = std::allocator<iter-value-type<InputIterator>>> concurrent_node_set(InputIterator, InputIterator, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> concurrent_node_set<iter-value-type<InputIterator>, Hash, Pred, Allocator>; template<class T, class Hash = boost::hash<T>, class Pred = std::equal_to<T>, class Allocator = std::allocator<T>> concurrent_node_set(std::initializer_list<T>, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> concurrent_node_set<T, Hash, Pred, Allocator>; template<class InputIterator, class Allocator> concurrent_node_set(InputIterator, InputIterator, typename see below::size_type, Allocator) -> concurrent_node_set<iter-value-type<InputIterator>, boost::hash<iter-value-type<InputIterator>>, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class InputIterator, class Allocator> concurrent_node_set(InputIterator, InputIterator, Allocator) -> concurrent_node_set<iter-value-type<InputIterator>, boost::hash<iter-value-type<InputIterator>>, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class InputIterator, class Hash, class Allocator> concurrent_node_set(InputIterator, InputIterator, typename see below::size_type, Hash, Allocator) -> concurrent_node_set<iter-value-type<InputIterator>, Hash, std::equal_to<iter-value-type<InputIterator>>, Allocator>; template<class T, class Allocator> concurrent_node_set(std::initializer_list<T>, typename see below::size_type, Allocator) -> concurrent_node_set<T, boost::hash<T>, std::equal_to<T>, Allocator>; template<class T, class Allocator> concurrent_node_set(std::initializer_list<T>, Allocator) -> concurrent_node_set<T, boost::hash<T>, std::equal_to<T>, Allocator>; template<class T, class Hash, class Allocator> concurrent_node_set(std::initializer_list<T>, typename see below::size_type, Hash, Allocator) -> concurrent_node_set<T, Hash, std::equal_to<T>, Allocator>; // Equality Comparisons template<class Key, class Hash, class Pred, class Alloc> bool operator==(const concurrent_node_set<Key, Hash, Pred, Alloc>& x, const concurrent_node_set<Key, Hash, Pred, Alloc>& y); template<class Key, class Hash, class Pred, class Alloc> bool operator!=(const concurrent_node_set<Key, Hash, Pred, Alloc>& x, const concurrent_node_set<Key, Hash, Pred, Alloc>& y); // swap template<class Key, class Hash, class Pred, class Alloc> void swap(concurrent_node_set<Key, Hash, Pred, Alloc>& x, concurrent_node_set<Key, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y))); // Erasure template<class K, class H, class P, class A, class Predicate> typename concurrent_node_set<K, H, P, A>::size_type erase_if(concurrent_node_set<K, H, P, A>& c, Predicate pred); // Pmr aliases (C++17 and up) namespace unordered::pmr { template<class Key, class Hash = boost::hash<Key>, class Pred = std::equal_to<Key>> using concurrent_node_set = boost::concurrent_node_set<Key, Hash, Pred, std::pmr::polymorphic_allocator<Key>>; } }
描述
模板参数
键 |
|
哈希 |
一个一元函数对象类型,充当 |
Pred |
一个二元函数对象,它在 |
分配器 |
其值类型与表的值类型相同的分配器。 |
表的元素节点保存在内部的_桶数组_中。节点插入到由其元素哈希码确定的桶中,但如果桶已被占用(_冲突_),则使用原始位置附近的一个可用桶。
桶数组的大小可以通过调用 `insert`/`emplace` 自动增加,或者通过调用 `rehash`/`reserve` 来增加。表的负载因子(元素数量除以桶的数量)永远不会大于 `max_load_factor()`,除非在较小的尺寸下,实现可能决定允许更高的负载。
如果hash_is_avalanching<Hash>::value
为true
,则哈希函数按原样使用;否则,会添加一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。
并发需求和保证
对同一 `Hash` 或 `Pred` 常量实例的并发调用 `operator()` 不会引入数据竞争。对于 `Alloc` 是 `Allocator` 或从 `Allocator` 重新绑定的任何分配器类型,对同一 `Alloc` 实例 `al` 的以下操作的并发调用不会引入数据竞争。
-
从 `al` 复制构造从 `Alloc` 重新绑定的分配器
-
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` 的非 const 容器函数中,针对最后一个(或唯一一个)访问函数。
-
任何插入或修改元素e
的boost::concurrent_node_set
操作都将与访问函数对e
的内部调用同步。
boost::concurrent_node_set
x
执行的访问函数不允许调用x
上的任何操作;只有在y
上并发未完成的操作不会直接或间接访问x
时,才允许调用不同的boost::concurrent_node_set
实例y
上的操作。
配置宏
BOOST_UNORDERED_DISABLE_REENTRANCY_CHECK
在调试版本中(更准确地说,当 BOOST_ASSERT_IS_VOID
未定义时),会检测到容器重入(从访问 `m` 元素的函数中非法调用 `m` 上的操作)并通过 `BOOST_ASSERT_MSG` 发出信号。当运行时速度成为关注点时,可以通过全局定义此宏来禁用此功能。
类型定义
typedef implementation-defined node_type;
一个用于保存提取的表元素的类,模拟NodeHandle。
typedef implementation-defined insert_return_type;
内部类模板的特化
template<class NodeType>
struct insert_return_type // name is exposition only
{
bool inserted;
NodeType node;
};
其中NodeType
= node_type
。
构造函数
默认构造函数
concurrent_node_set();
使用 `hasher()` 作为哈希函数、`key_equal()` 作为键相等谓词和 `allocator_type()` 作为分配器构造一个空表。
后置条件 |
|
要求 |
如果使用默认值,则 |
桶计数构造函数
explicit concurrent_node_set(size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用 `hf` 作为哈希函数、`eql` 作为键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。
后置条件 |
|
要求 |
如果使用默认值,则 |
迭代器范围构造函数
template<class InputIterator>
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());
使用 `hf` 作为哈希函数、`eql` 作为键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表,并将元素从 `[f, l)` 插入到其中。
要求 |
如果使用默认值,则 |
复制构造函数
concurrent_node_set(concurrent_node_set const& other);
复制构造函数。复制包含的元素、哈希函数、谓词和分配器。
如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。
要求 |
`value_type` 可复制构造。 |
并发性 |
阻塞 |
移动构造函数
concurrent_node_set(concurrent_node_set&& other);
移动构造函数。other
的内部桶数组直接转移到新表。哈希函数、谓词和分配器从 other
移动构造。如果启用了统计信息 enabled,则从 other
转移内部统计信息并调用 other.reset_stats()
。
并发性 |
阻塞 |
带分配器的迭代器范围构造函数
template<class InputIterator>
concurrent_node_set(InputIterator f, InputIterator l, const allocator_type& a);
使用 `a` 作为分配器,使用默认哈希函数和键相等谓词构造一个空表,并将元素从 `[f, l)` 插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带分配器的复制构造函数
concurrent_node_set(concurrent_node_set const& other, Allocator const& a);
构造一个表,复制 `other` 的包含元素、哈希函数和谓词,但使用分配器 `a`。
并发性 |
阻塞 |
带分配器的移动构造函数
concurrent_node_set(concurrent_node_set&& other, Allocator const& a);
如果 a == other.get_allocator()
,则 other
的元素直接转移到新表;否则,元素从 other
的元素移动构造。哈希函数和谓词从 other
移动构造,分配器从 a
复制构造。如果启用了统计信息 enabled,则当且仅当 a == other.get_allocator()
时转移内部统计信息,并且始终调用 other.reset_stats()
。
并发性 |
阻塞 |
从 unordered_node_set 的移动构造函数
concurrent_node_set(unordered_node_set<Key, Hash, Pred, Allocator>&& other);
从 unordered_node_set
的移动构造。other
的内部桶数组直接转移到新的容器。哈希函数、谓词和分配器从 other
移动构造。如果启用了统计信息 enabled,则从 other
转移内部统计信息并调用 other.reset_stats()
。
复杂度 |
O( |
初始化列表构造函数
concurrent_node_set(std::initializer_list<value_type> il,
size_type n = implementation-defined
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
使用 `hf` 作为哈希函数、`eql` 作为键相等谓词和 `a` 构造一个至少具有 `n` 个桶的空表,并将元素从 `il` 插入到其中。
要求 |
如果使用默认值,则 |
带分配器的桶计数构造函数
concurrent_node_set(size_type n, allocator_type const& a);
使用 `hf` 作为哈希函数、默认哈希函数和键相等谓词以及 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。
后置条件 |
|
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带哈希函数和分配器的桶计数构造函数
concurrent_node_set(size_type n, hasher const& hf, allocator_type const& a);
使用 `hf` 作为哈希函数、默认键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。
后置条件 |
|
要求 |
`key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的迭代器范围构造函数
template<class InputIterator>
concurrent_node_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a);
使用 `a` 作为分配器和默认哈希函数和键相等谓词构造一个至少具有 `n` 个桶的空表,并将元素从 `[f, l)` 插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和哈希函数的迭代器范围构造函数
template<class InputIterator>
concurrent_node_set(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a);
使用 `hf` 作为哈希函数、`a` 作为分配器、默认键相等谓词构造一个至少具有 `n` 个桶的空表,并将元素从 `[f, l)` 插入到其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
带分配器的初始化列表构造函数
concurrent_node_set(std::initializer_list<value_type> il, const allocator_type& a);
使用 `a` 和默认哈希函数和键相等谓词构造一个空表,并将元素从 `il` 插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数和分配器的初始化列表构造函数
concurrent_node_set(std::initializer_list<value_type> il, size_type n, const allocator_type& a);
使用 `a` 和默认哈希函数和键相等谓词构造一个至少具有 `n` 个桶的空表,并将元素从 `il` 插入到其中。
要求 |
`hasher` 和 `key_equal` 需要是 可默认构造的。 |
带桶计数、哈希函数和分配器的初始化列表构造函数
concurrent_node_set(std::initializer_list<value_type> il, size_type n, const hasher& hf,
const allocator_type& a);
使用 `hf` 作为哈希函数、`a` 作为分配器和默认键相等谓词构造一个至少具有 `n` 个桶的空表,并将元素从 `il` 插入到其中。
要求 |
`key_equal` 需要是 可默认构造的。 |
赋值
复制赋值
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
元素的副本。
要求 |
|
并发性 |
阻塞于 `*this` 和 `other`。 |
移动赋值
concurrent_node_set& operator=(concurrent_node_set&& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::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
元素的移动构造副本。如果启用了统计信息 enabled,则当且仅当最终分配器等于 other.get_allocator()
时转移内部统计信息,并且始终调用 other.reset_stats()
。
并发性 |
阻塞于 `*this` 和 `other`。 |
访问
[c]visit
template<class F> size_t visit(const key_type& k, F f);
template<class F> size_t visit(const key_type& k, F f) const;
template<class F> size_t cvisit(const key_type& k, F f) const;
template<class K, class F> size_t visit(const K& k, F f);
template<class K, class F> size_t visit(const K& k, F f) const;
template<class K, class F> size_t cvisit(const K& k, F f) const;
如果存在键与k
等效的元素x
,则使用对x
的常量引用调用f
。
返回值 |
访问的元素数量(0 或 1)。 |
注释 |
只有当 `Hash::is_transparent` 和 `Pred::is_transparent` 是有效的成员类型定义时,`template<class K, class F>` 重载才会参与重载解析。库假设 `Hash` 可用 `K` 和 `Key` 调用,并且 `Pred` 是透明的。这允许异构查找,避免了实例化 `Key` 类型实例的成本。 |
批量访问
template<class FwdIterator, class F>
size_t visit(FwdIterator first, FwdIterator last, F f);
template<class FwdIterator, class F>
size_t visit(FwdIterator first, FwdIterator last, F f) const;
template<class FwdIterator, class F>
size_t cvisit(FwdIterator first, FwdIterator last, F f) const;
对于范围[first
, last
)中的每个元素k
,如果容器中存在键与k
等效的元素x
,则使用对x
的常量引用调用f
。
虽然在功能上等效于对每个键分别调用 [c]visit
,但由于内部简化优化,批量访问通常执行速度更快。建议 std::distance(first,last)
至少为 bulk_visit_size
以获得性能提升:超过此大小,性能预计不会进一步提高。
要求 |
|
返回值 |
已访问元素的数量。 |
[c]visit_all
template<class F> size_t visit_all(F f);
template<class F> size_t visit_all(F f) const;
template<class F> size_t cvisit_all(F f) const;
连续使用对表中每个元素的常量引用调用f
。
返回值 |
已访问元素的数量。 |
并行 [c]visit_all
template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f);
template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f) const;
template<class ExecutionPolicy, class F> void cvisit_all(ExecutionPolicy&& policy, F f) const;
使用对表中每个元素的常量引用调用f
。根据指定的执行策略的语义并行执行。
抛出 |
根据所用执行策略的异常处理机制,如果在 |
注释 |
仅在支持C++17并行算法的编译器中可用。 只有当 不允许无序执行策略。 |
[c]visit_while
template<class F> bool visit_while(F f);
template<class F> bool visit_while(F f) const;
template<class F> bool cvisit_while(F f) const;
连续使用对表中每个元素的常量引用调用f
,直到f
返回false
或访问所有元素。
返回值 |
如果 |
并行 [c]visit_while
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f);
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f) const;
template<class ExecutionPolicy, class F> bool cvisit_while(ExecutionPolicy&& policy, F f) const;
使用对表中每个元素的常量引用调用f
,直到f
返回false
或访问所有元素。根据指定的执行策略的语义并行执行。
返回值 |
如果 |
抛出 |
根据所用执行策略的异常处理机制,如果在 |
注释 |
仅在支持C++17并行算法的编译器中可用。 只有当 不允许无序执行策略。 并行化意味着执行并不一定在 |
修改器
emplace
template<class... Args> bool emplace(Args&&... args);
仅当表中不存在具有等效键的元素时,才使用参数args
构造的对象插入到表中。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
复制插入
bool insert(const value_type& obj);
仅当表中不存在具有等效键的元素时,才将obj
插入到表中。
要求 |
`value_type` 可 复制插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
移动插入
bool insert(value_type&& obj);
仅当表中不存在具有等效键的元素时,才将obj
插入到表中。
要求 |
`value_type` 可 移动插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
透明插入
template<class K> bool insert(K&& k);
当且仅当容器中没有具有等效键的元素时,才将从std::forward<K>(k)
构造的元素插入到容器中。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
只有当 |
插入迭代器范围
template<class InputIterator> size_type insert(InputIterator first, InputIterator last);
等价于
while(first != last) this->emplace(*first++);
返回值 |
插入的元素数量。 |
插入初始化列表
size_type insert(std::initializer_list<value_type> il);
等价于
this->insert(il.begin(), il.end());
返回值 |
插入的元素数量。 |
插入节点
insert_return_type insert(node_type&& nh);
如果 nh
不为空,当且仅当表中没有键与 nh.value()
等效的元素时,才将关联元素插入到表中。函数返回时,nh
为空。
返回值 |
由
|
抛出 |
如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。 |
并发性 |
阻塞 |
注释 |
如果 |
emplace_or_[c]visit
template<class... Args, class F> bool emplace_or_visit(Args&&... args, F&& f);
template<class... Args, class F> bool emplace_or_cvisit(Args&&... args, F&& f);
如果表中没有键等效的元素,则使用参数args
构造的对象插入到表中。否则,使用对等效元素的常量引用调用f
。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
该接口仅用于说明,因为C++不允许在可变参数包之后声明参数 |
复制 insert_or_[c]visit
template<class F> bool insert_or_visit(const value_type& obj, F f);
template<class F> bool insert_or_cvisit(const value_type& obj, F f);
当且仅当表中没有键等效的元素时,才将obj
插入到表中。否则,使用对等效元素的常量引用调用f
。
要求 |
`value_type` 可 复制插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
移动 insert_or_[c]visit
template<class F> bool insert_or_visit(value_type&& obj, F f);
template<class F> bool insert_or_cvisit(value_type&& obj, F f);
当且仅当表中没有键等效的元素时,才将obj
插入到表中。否则,使用对等效元素的常量引用调用f
。
要求 |
`value_type` 可 移动插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
透明insert_or_[c]visit
template<class K, class F> bool insert_or_visit(K&& k, F f);
template<class K, class F> bool insert_or_cvisit(K&& k, F f);
当且仅当容器中没有键等效的元素时,才将从std::forward<K>(k)
构造的元素插入到容器中。否则,使用对等效元素的常量引用调用f
。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
只有当 |
插入迭代器范围或访问
template<class InputIterator,class F>
size_type insert_or_visit(InputIterator first, InputIterator last, F f);
template<class InputIterator,class F>
size_type insert_or_cvisit(InputIterator first, InputIterator last, F f);
等价于
while(first != last) this->emplace_or_[c]visit(*first++, f);
返回值 |
插入的元素数量。 |
插入初始化列表或访问
template<class F> size_type insert_or_visit(std::initializer_list<value_type> il, F f);
template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> il, F f);
等价于
this->insert_or_[c]visit(il.begin(), il.end(), std::ref(f));
返回值 |
插入的元素数量。 |
插入节点或访问
template<class F> insert_return_type insert_or_visit(node_type&& nh, F f);
template<class F> insert_return_type insert_or_cvisit(node_type&& nh, F f);
如果 nh
为空,则不执行任何操作。否则,当且仅当表中没有键与 nh.value()
等效的元素时,才将关联元素插入到表中。否则,使用对等元素的常量引用调用 f
。
返回值 |
由
|
抛出 |
如果操作(而不是对 |
并发性 |
阻塞 |
注释 |
如果 |
emplace_and_[c]visit
template<class... Args, class F1, class F2>
bool emplace_or_visit(Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool emplace_or_cvisit(Args&&... args, F1&& f1, F2&& f2);
如果表中没有键等效的元素,则使用参数args
构造的对象插入到表中,然后使用对新创建元素的常量引用调用f1
。否则,使用对等效元素的常量引用调用f2
。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
该接口仅用于说明,因为C++不允许在可变参数包之后声明参数 |
复制 insert_and_[c]visit
template<class F1, class F2> bool insert_and_visit(const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(const value_type& obj, F1 f2, F2 f2);
当且仅当表中没有具有等效键的元素时,才将 obj
插入到表中,然后使用新创建元素的常量引用调用 f1
。否则,使用对等元素的常量引用调用 f
。
要求 |
`value_type` 可 复制插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
移动 insert_and_[c]visit
template<class F1, class F2> bool insert_and_visit(value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2);
当且仅当表中没有键等效的元素时,才将obj
插入到表中,然后使用对新创建元素的常量引用调用f1
。否则,使用对等效元素的常量引用调用f2
。
要求 |
`value_type` 可 移动插入。 |
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
透明insert_and_[c]visit
template<class K, class F1, class F2> bool insert_and_visit(K&& k, F1 f1, F2 f2);
template<class K, class F1, class F2> bool insert_and_cvisit(K&& k, F1 f1, F2 f2);
当且仅当容器中没有键等效的元素时,才将从std::forward<K>(k)
构造的元素插入到容器中,然后使用对新创建元素的常量引用调用f1
。否则,使用对等效元素的常量引用调用f2
。
要求 |
|
返回值 |
如果进行了插入,则为 |
并发性 |
阻塞 |
注释 |
只有当 |
插入迭代器范围和访问
template<class InputIterator,class F1, class F2>
size_type insert_and_visit(InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class InputIterator,class F1, class f2>
size_type insert_and_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2);
等价于
while(first != last) this->emplace_and_[c]visit(*first++, f1, f2);
返回值 |
插入的元素数量。 |
插入初始化列表和访问
template<class F1, class F2>
size_type insert_and_visit(std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
size_type insert_and_cvisit(std::initializer_list<value_type> il, F1 f1, F2 f2);
等价于
this->insert_and_[c]visit(il.begin(), il.end(), std::ref(f1), std::ref(f2));
返回值 |
插入的元素数量。 |
插入节点并访问
template<class F1, class F2>
insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2);
template<class F1, class F2>
insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2);
如果 nh
为空,则不执行任何操作。否则,当且仅当表中没有键与 nh.value()
等效的元素时,才将关联元素插入到表中,然后使用新插入元素的常量引用调用 f1
。否则,使用对等元素的常量引用调用 f2
。
返回值 |
由
|
抛出 |
如果操作(而不是对 |
并发性 |
阻塞 |
注释 |
如果 |
erase
size_type erase(const key_type& k);
template<class K> size_type erase(const K& k);
如果存在,则删除键与k
等效的元素。
返回值 |
删除的元素数量(0或1)。 |
抛出 |
只有当 |
注释 |
只有当 |
按键删除
template<class F> size_type erase_if(const key_type& k, F f);
template<class K, class F> size_type erase_if(const K& k, F f);
如果存在,并且f(x)
为true
,则删除键与k
等效的元素x
。
返回值 |
删除的元素数量(0或1)。 |
抛出 |
只有当 |
注释 |
只有当 只有当 |
erase_if
template<class F> size_type erase_if(F f);
连续地使用对表中每个元素的引用调用f
,并删除f
返回true
的那些元素。
返回值 |
删除的元素数量。 |
抛出 |
只有当 |
并行 erase_if
template<class ExecutionPolicy, class F> void erase_if(ExecutionPolicy&& policy, F f);
使用对表中每个元素的引用调用f
,并删除f
返回true
的那些元素。根据指定的执行策略的语义并行执行。
抛出 |
根据所用执行策略的异常处理机制,如果在 |
注释 |
仅在支持C++17并行算法的编译器中可用。 只有当 不允许无序执行策略。 |
交换 (swap)
void swap(concurrent_node_set& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_swap::value);
将表的内容与参数交换。
如果声明了Allocator::propagate_on_container_swap
并且Allocator::propagate_on_container_swap::value
为true
,则交换表的分配器。否则,使用不相等的分配器进行交换会导致未定义的行为。
抛出 |
除非 |
并发性 |
阻塞于 `*this` 和 `other`。 |
提取
node_type extract(const key_type& k);
template<class K> node_type extract(K&& k);
提取键与 k
等效的元素(如果存在)。
返回值 |
包含提取元素的 |
抛出 |
只有当 |
注释 |
只有当 |
条件提取
template<class F> node_type extract_if(const key_type& k, F f);
template<class K, class F> node_type extract_if(K&& k, F f);
如果存在元素x
且其键等效于k
,并且f(x)
为true
,则提取该元素。
返回值 |
包含提取元素的 |
抛出 |
仅当 |
注释 |
只有当 |
清空 (clear)
void clear() noexcept;
删除表中的所有元素。
后置条件 |
|
并发性 |
阻塞于 `*this`。 |
合并 (merge)
template<class H2, class P2>
size_type merge(concurrent_node_set<Key, H2, P2, Allocator>& source);
template<class H2, class P2>
size_type merge(concurrent_node_set<Key, H2, P2, Allocator>&& source);
将 source
中所有键在 *this
中不存在的元素移动插入到 *this
中,并从 source
中删除这些元素。
返回值 |
插入的元素数量。 |
并发性 |
阻塞 |
集合操作
计数 (count)
size_type count(const key_type& k) const;
template<class K>
size_type count(const K& k) const;
返回值 |
键与 |
注释 |
只有当 在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。 |
包含 (contains)
bool contains(const key_type& k) const;
template<class K>
bool contains(const K& k) const;
返回值 |
一个布尔值,指示表中是否存在键等于 |
注释 |
只有当 在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。 |
哈希策略
负载因子 (load_factor)
float load_factor() const noexcept;
返回值 |
|
设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果 |
不执行任何操作,因为不允许用户更改此参数。为了与 |
最大负载
size_type max_load() const noexcept;
返回值 |
在不重新哈希的情况下,表可以容纳的最大元素数量,假设不会再删除任何元素。 |
注意 |
构造、重新哈希或清除后,表的最大负载至少为 在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。 |
重新哈希 (rehash)
void rehash(size_type n);
如有必要,更改桶数组的大小,使其至少有n
个桶,并且负载因子小于或等于最大负载因子。在适用情况下,这将扩大或缩小与表关联的bucket_count()
。
当size() == 0
时,rehash(0)
将释放底层桶数组。
抛出 |
如果抛出异常,则该函数无效,除非异常是由表的哈希函数或比较函数抛出的。 |
并发性 |
阻塞于 `*this`。 |
预留空间 (reserve)
void reserve(size_type n);
等效于 a.rehash(ceil(n / a.max_load_factor()))
。
类似于rehash
,此函数可用于增加或减少表中桶的数量。
抛出 |
如果抛出异常,则该函数无效,除非异常是由表的哈希函数或比较函数抛出的。 |
并发性 |
阻塞于 `*this`。 |
推导指南
如果出现以下任何情况,则推导指南不会参与重载解析:
-
它具有
InputIterator
模板参数,并且为该参数推导的类型不符合输入迭代器的资格。 -
它具有
Allocator
模板参数,并且为该参数推导的类型不符合分配器的资格。 -
它具有
Hash
模板参数,并且为该参数推导的类型为整数类型或符合分配器资格的类型。 -
它具有
Pred
模板参数,并且为该参数推导的类型符合分配器资格的类型。
推导指南中的size_type
参数类型引用由推导指南推导出的容器类型的size_type
成员类型。其默认值与所选构造函数的默认值一致。
迭代器值类型 (iter-value-type)
template<class InputIterator> using iter-value-type = typename std::iterator_traits<InputIterator>::value_type; // exposition only
相等比较
相等运算符 (operator==)
template<class Key, class Hash, class Pred, class Alloc>
bool operator==(const concurrent_node_set<Key, Hash, Pred, Alloc>& x,
const concurrent_node_set<Key, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个键相同、值相等(使用operator==
比较值类型)的元素,则返回true
。
并发性 |
阻塞 |
注释 |
如果两个表没有等效的相等谓词,则行为未定义。 |
不相等运算符 (operator!=)
template<class Key, class Hash, class Pred, class Alloc>
bool operator!=(const concurrent_node_set<Key, Hash, Pred, Alloc>& x,
const concurrent_node_set<Key, Hash, Pred, Alloc>& y);
如果x.size() == y.size()
,并且对于x
中的每个元素,在y
中都有一个键相同、值相等(使用operator==
比较值类型)的元素,则返回false
。
并发性 |
阻塞 |
注释 |
如果两个表没有等效的相等谓词,则行为未定义。 |
交换
template<class Key, class Hash, class Pred, class Alloc>
void swap(concurrent_node_set<Key, Hash, Pred, Alloc>& x,
concurrent_node_set<Key, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
等价于
x.swap(y);
erase_if
template<class K, class H, class P, class A, class Predicate>
typename concurrent_node_set<K, H, P, A>::size_type
erase_if(concurrent_node_set<K, H, P, A>& c, Predicate pred);
等价于
c.erase_if(pred);
序列化
可以使用此库提供的 API 通过 Boost.Serialization 存档/检索 concurrent_node_set
。支持常规存档和 XML 存档。
将 concurrent_node_set 保存到存档
将 concurrent_node_set
x
的所有元素保存到存档(XML 存档)ar
中。
要求 |
|
并发性 |
阻塞 |
从存档加载 concurrent_node_set
删除 concurrent_node_set
x
中所有预先存在的元素,并从存档(XML 存档)ar
中插入原始 concurrent_node_set
other
元素的已恢复副本(这些副本已保存到 ar
读取的存储中)。
要求 |
|
并发性 |
阻塞 |
变更日志
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
操作中使访问成为独占锁,以允许安全地修改元素 (PR#265)。 -
在 Visual Studio Natvis 中,支持使用花哨指针的分配器的任何容器。只要为花哨指针类型编写了正确的 Natvis 自定义点“Intrinsic”函数,这便适用于任何花哨指针类型。
-
为所有容器和迭代器添加了 GDB pretty-printers。对于使用花哨指针的分配器的容器,只有为花哨指针类型本身编写了正确的 pretty-printer 时,这些才有效。
-
修复了开放寻址容器的
std::initializer_list
赋值问题 (PR#277)。 -
允许将不可复制的可调用对象传递给并发容器的
insert_{and|or}_[c]visit
的std::initializer_list
重载,方法是将可调用对象的std::reference_wrapper
在内部传递给迭代器对重载。
1.86.0 版本
-
当提供头文件
<memory_resource>
时,添加容器pmr
别名。别名boost::unordered::pmr::[container]
指的是使用std::pmr::polymorphic_allocator
分配器类型的boost::unordered::[container]
。 -
配备开放寻址和并发容器以内部计算和提供受哈希函数质量影响的统计指标。此功能通过全局宏
BOOST_UNORDERED_ENABLE_STATS
启用。 -
雪崩哈希函数现在必须通过
is_avalanching
typedef 标记,该 typedef 具有设置为true
的嵌入式value
常量(通常,将is_avalanching
定义为std::true_type
)。using is_avalanching = void
已弃用,但为了向后兼容性而允许。 -
为容器和迭代器添加了 Visual Studio Natvis 框架自定义可视化。这适用于使用原始指针的分配器的所有容器。在此版本中,如果其分配器使用花哨指针,则不支持容器和迭代器。这可能会在以后的版本中得到解决。
1.84.0 版本 - 主要更新
-
添加了
boost::concurrent_flat_set
。 -
向并发容器添加了
[c]visit_while
操作,包括串行和并行变体。 -
添加了从
boost::concurrent_flat_(map|set)
到boost::unordered_flat_(map|set)
以及反向的有效移动构造。 -
为并发容器添加了批量访问,以提高查找性能。
-
添加了用于检测用户代码中对并发容器的非法重入的调试模式机制。
-
向所有容器及其(非局部)迭代器类型添加了 Boost.Serialization 支持。
-
为开放寻址和并发容器添加了对花哨指针的支持。这使得可以使用 Boost.Interprocess 分配器在共享内存中构造容器。
-
修复了闭合寻址容器的局部迭代器的成员指针运算符中的错误 (PR#221,感谢 GitHub 用户 vslashg 发现并修复了此问题)。
-
从本版本开始,
boost::unordered_[multi]set
和boost::unordered_[multi]map
仅适用于 C++11 及更高版本。
1.83.0 版本 - 主要更新
-
添加了
boost::concurrent_flat_map
,这是一种基于开放寻址的快速、线程安全的哈希映射。 -
加快了开放寻址容器的迭代速度。
-
在开放寻址容器中,以前不返回任何内容的
erase(iterator)
现在返回一个可转换为指向下一个元素的迭代器的代理对象。这使得无需在不使用返回的代理时产生任何性能损失即可使用典型的it = c.erase(it)
习惯用法。
1.82.0 版本 - 主要更新
-
计划弃用 C++03 支持。Boost 1.84.0 将不再支持 C++03 模式,C++11 将成为使用该库的新最低要求。
-
添加了基于节点的开放寻址容器
boost::unordered_node_map
和boost::unordered_node_set
。 -
根据 P2363 将异构查找扩展到更多成员函数。
-
将开放寻址容器以前的混合后处理过程替换为基于常数扩展乘法的新的算法。
-
修复了内部
emplace()
实现中的错误,其中堆栈局部类型未使用容器的分配器正确构造,这会破坏使用分配器构造。
1.81.0 版本 - 主要更新
-
添加了基于开放寻址的快速容器
boost::unordered_flat_map
和boost::unordered_flat_set
。 -
为所有容器添加了 CTAD 推导指南。
-
添加了 LWG issue 2713 中指定的缺失构造函数。
1.80.0 版本 - 主要更新
-
重构内部实现,使其速度大幅提升
-
允许使用
final
Hasher 和 KeyEqual 对象 -
更新文档,添加基准图和关于新的内部数据结构的说明
1.79.0 版本
-
改进了 C++20 支持
-
所有容器都已更新为支持异构
count
、equal_range
和find
。 -
所有容器现在都实现了成员函数
contains
。 -
已为所有容器实现
erase_if
。
-
-
改进了 C++23 支持
-
所有容器都已更新为支持异构
erase
和extract
。
-
-
更改了
reserve
的行为,使其积极分配 (PR#59)。 -
测试套件中的各种警告修复。
-
更新代码以内部使用
boost::allocator_traits
。 -
切换到斐波那契散列。
-
更新文档,使用 AsciiDoc 代替 QuickBook 编写。
1.67.0 版本
-
改进了 C++17 支持
-
添加标准中的模板推导指南。
-
在节点句柄中使用简单的
optional
实现,使它们更接近标准。 -
向
swap
、operator=
和节点句柄添加缺失的noexcept
说明,并更改实现以匹配。在实现中使用std::allocator_traits::is_always_equal
(或在不可用时使用我们自己的实现)和boost::is_nothrow_swappable
。
-
-
改进了 C++20 支持
-
使用
boost::to_address
(具有建议的 C++20 语义),而不是旧的自定义实现。
-
-
向迭代器添加
element_type
,以便std::pointer_traits
可以工作。 -
在最近版本的 Visual C++ 和其他使用 Dinkumware 标准库的地方使用
std::piecewise_construct
,现在使用 Boost.Predef 检查编译器和库版本。 -
使用
std::iterator_traits
而不是 boost 迭代器特性,以消除对 Boost.Iterator 的依赖。 -
删除迭代器从
std::iterator
的继承,这在 C++17 中已被弃用,感谢 Daniela Engert (PR#7)。 -
停止使用
BOOST_DEDUCED_TYPENAME
。 -
更新一些 Boost 包含路径。
-
重命名一些内部方法和变量。
-
各种测试改进。
-
各种内部更改。
1.65.0 版本
-
为
quick_erase
和erase_return_void
添加弃用属性。这次我确实会在未来的版本中移除它们。 -
少量标准兼容性修正
-
swap
自由函数的noexpect
规范。 -
添加缺失的
insert(P&&)
方法。
-
1.64.0 版本
-
初步支持新的C++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++中放置字符串字面量的问题。 -
修复赋值操作中的异常安全问题。如果桶分配抛出异常,它可能会覆盖哈希和相等函数,同时保留现有的元素。这意味着函数对象将与容器元素不匹配,因此元素可能位于错误的桶中,并且等效元素将被错误地处理。
-
各种参考文档改进。
-
更好的分配器支持 (#12459).
-
使无参数构造函数隐式。
-
实现缺失的分配器感知构造函数。
-
修复空容器的哈希/键相等函数的赋值。
-
从文档中的示例中移除unary/binary_function。它们在C++17中已被移除。
-
支持emplace中的10个构造函数参数。它原本打算支持最多10个参数,但预处理器代码中的一个越界错误导致它只支持最多9个。
1.62.0 版本
-
移除对已弃用的
boost::iterator
的使用。 -
移除
BOOST_NO_STD_DISTANCE
解决方法。 -
移除
BOOST_UNORDERED_DEPRECATED_EQUALITY
警告。 -
更简单的赋值实现,修复了
unordered_multiset
和unordered_multimap
的异常安全问题。可能会稍微慢一些。 -
停止使用某些旧编译器存在问题的返回值SFINAE。
1.54.0 版本
-
将标准中指定的函数标记为
noexpect
。更多内容将在下一个版本中发布。 -
如果哈希函数和相等谓词都已知具有nothrow移动赋值或构造,则使用它们。
1.53.0 版本
-
移除对旧的预标准可变参数对构造函数和相等实现的支持。自Boost 1.48以来,两者均已弃用。
-
移除对已弃用的配置宏的使用。
-
更多内部实现更改,包括更简单的
erase
实现。
1.52.0 版本
-
更快的赋值,它尽可能地赋值给现有节点,而不是创建全新的节点和复制构造。
-
修复
erase_range
中的错误 (#7471). -
恢复了一些关于如何创建节点的内部更改,特别是对于C++11编译器。“construct”和“destroy”应该对C++11分配器更好地工作。
-
稍微简化了实现。希望更健壮。
1.50.0 版本
-
修复
unordered_multiset
和unordered_multimap
的相等性。 -
工单 6857:实现
reserve
。 -
工单 6771:避免gcc的
-Wfloat-equal
警告。 -
工单 6784:修复一些Sun特有的代码。
-
工单 6190:避免gcc的
-Wshadow
警告。 -
工单 6905:使宏中的命名空间与
bcp
自定义命名空间兼容。由Luke Elliott修复。 -
移除一些较小的素数桶,因为它们可能会使冲突的概率相当高(例如,5的倍数非常常见,因为我们使用了10进制)。
-
在旧版本的Visual C++上,使用容器库的
allocator_traits
实现,因为它更有可能工作。 -
在具有64位std::size_t的机器上,使用2的幂次方桶,并使用Thomas Wang的哈希函数来选择使用哪个桶。因为对于64位值,取模非常慢。
-
一些内部更改。
1.49.0 版本
-
修复由于意外的奇数赋值导致的警告。
-
稍微更好的错误消息。
1.48.0 版本 - 主要更新
这是一个重大更改,它已转换为使用Boost.Move的移动模拟,并更符合C++11标准。有关详细信息,请参见兼容性部分。
容器现在满足C++11的复杂性要求,但为此使用了更多的内存。这意味着quick_erase
和erase_return_void
不再需要,它们将在未来的版本中移除。
C++11支持导致了一些重大更改
-
相等比较已更改为C++11规范。在具有等效键的容器中,具有相等键的组中的元素以前必须具有相同的顺序才能被视为相等,现在它们可以是彼此的排列。要使用旧的行为,请定义宏
BOOST_UNORDERED_DEPRECATED_EQUALITY
。 -
当要交换的两个容器具有不相等的分配器时,交换的行为有所不同。它以前使用适当的分配器分配新的节点,现在如果分配器具有成员结构
propagate_on_container_swap
,并且propagate_on_container_swap::value
为true,则交换分配器。 -
分配器的
construct
和destroy
函数使用原始指针调用,而不是分配器的pointer
类型。 -
emplace
以前模拟了出现在早期C++0x草案中的可变参数对构造函数。由于它们已被移除,它不再这样做。它确实模拟了新的piecewise_construct
对构造函数——只需要使用boost::piecewise_construct
。要使用旧的可变参数构造函数模拟,请定义BOOST_UNORDERED_DEPRECATED_PAIR_CONSTRUCT
。
1.45.0 版本
-
修复使用返回按复制方式返回
value_type
的迭代器插入unordered_map
或unordered_set
时的错误。
1.43.0 版本
-
工单 3966:
erase_return_void
现在是quick_erase
,它是解决通过迭代器缓慢擦除的当前前兆,尽管将来很可能会有所改变。旧的方法名称保留是为了向后兼容性,但它被认为已弃用,并将在此后的版本中删除。 -
使用Boost.Exception。
-
停止使用已弃用的
BOOST_HAS_*
宏。
1.41.0 版本 - 主要更新
-
原始版本大量使用宏来规避一些旧编译器较差的模板支持。但是,由于我不再支持这些编译器,并且宏的使用开始成为维护负担,因此它已被重写为使用模板而不是宏来实现类。
-
由于对EBO使用
boost::compressed_pair
和略微不同的函数缓冲区(现在使用bool而不是成员指针),容器对象现在更小。 -
桶是延迟分配的,这意味着构造空容器不会分配任何内存。
1.38.0 版本
-
使用
boost::swap
。 -
工单 2237:说明如果两个对象的相等谓词不等效,则它们的相等和不相等运算符未定义。感谢Daniel Krügler。
-
工单 1710:使用更大的素数列表。感谢Thorsten Ottosen和Hervé Brönnimann。
-
使用对齐存储来存储类型。这改变了分配器用于构造节点的方式。它以前使用分配器的
construct
方法两次构造节点 - 一次用于指针,一次用于值。它现在使用一次构造调用构造节点,然后使用就地构造构造值。 -
在可用时添加对C++0x初始化列表的支持(目前仅限于C++0x模式下的g++ 4.4)。
1.37.0 版本
-
根据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日至2007年12月16日进行的评审。
参考文献
-
《C/C++ Users Journal》。2006年2月。Pete Becker。 STL和TR1:第三部分 - 无序容器。
标准无序容器简介。 -
《维基百科》。 哈希表。
哈希表实现简介。讨论了闭址法和开址法之间的区别。 -
Peter Dimov,2022年。 Boost.Unordered开发计划。
版权和许可
Daniel James
版权所有 © 2003, 2004 Jeremy B. Maitin-Shepard
版权所有 © 2005-2008 Daniel James
版权所有 © 2022-2023 Christian Mazakas
版权所有 © 2022-2024 Joaquín M López Muñoz
版权所有 © 2022-2023 Peter Dimov
版权所有 © 2024 Braden Ganetsky
根据Boost软件许可证版本1.0分发。(参见随附文件LICENSE_1_0.txt或复制自 https://boost.ac.cn/LICENSE_1_0.txt)