Boost C++ 库

……世界上最受推崇和设计精良的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu,《C++编码规范

引言

哈希表 是一种极其流行的计算机数据结构,几乎可以在任何编程语言中以某种形式找到。而其他关联结构,如 rb-树(在 C++ 中由std::setstd::map 使用)对插入和查找具有对数时间复杂度,如果配置正确,哈希表平均情况下可以以常数时间执行这些操作,并且通常速度要快得多。

C++ 在 C++11 中引入了 *无序关联容器* std::unordered_setstd::unordered_mapstd::unordered_multisetstd::unordered_multimap,但哈希表的研 究并没有停止:CPU 架构的进步,例如更强大的缓存、SIMD 操作和越来越多的多核处理器 ,为改进的基于哈希的数据结构和新的用例开辟了可能性,这些用例根本无法通过 2011 年指定的无序关联容器实现。

Boost.Unordered 提供了一个具有不同标准符合性级别、性能和预期使用场景的哈希容器目录。

表 1. Boost.Unordered 容器

基于节点的

扁平的

闭址

boost::unordered_set
boost::unordered_map
boost::unordered_multiset
boost::unordered_multimap

开址

boost::unordered_node_set
boost::unordered_node_map

boost::unordered_flat_set
boost::unordered_flat_map

并发

boost::concurrent_node_set
boost::concurrent_node_map

boost::concurrent_flat_set
boost::concurrent_flat_map

  • **闭址容器** 完全符合 C++ 无序关联容器的规范,并在所需标准接口施加的技术约束内具有市场上最快的实现之一。

  • **开址容器** 依赖于快得多的数据结构和算法(在典型情况下快两倍以上),同时略微偏离标准接口以适应实现。有两种变体:**扁平的**(最快的)和**基于节点的**,它们在重新哈希时提供指针稳定性,但代价是速度较慢。

  • 最后,**并发容器** 旨在用于高性能多线程场景。它们的接口与常规 C++ 容器的接口截然不同。提供了扁平的和基于节点的变体。

Boost.Unordered 中的所有集合和映射的实例化方式与std::unordered_setstd::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 个元素ABCDEboost::unordered_set(这只用于说明,容器通常会有更多桶)。

buckets

为了确定将元素放置在哪个桶中,容器将哈希函数Hash 应用于元素的键(对于集合,键是整个元素,但称为键以便可以对集合和映射使用相同的术语)。这将返回一个std::size_t 类型的数值。std::size_t 的数值范围远大于桶的数量,因此容器将另一个转换应用于该值以选择要将元素放置在其中的桶。

检索给定键的元素很简单。相同的过程应用于键以查找正确的桶。然后将键与桶中的元素进行比较以查找任何匹配的元素(使用相等谓词Pred)。如果哈希函数运行良好,则元素将均匀地分布在各个桶中,因此只需要检查少量元素。

您可以从图中看到AD 已被放置在同一个桶中。在查找此桶中的元素时,最多会进行 2 次比较,从而使搜索速度变慢。这称为**冲突**。为了保持速度,我们尽量将冲突降到最低。

如果我们使用boost::unordered_flat_set 而不是boost::unordered_set,则该图将如下所示。

buckets oa

在开址容器中,桶最多只能容纳一个元素;如果发生冲突(例如示例中的D),则该元素将使用原始位置附近的一些其他可用桶。鉴于这种情况更简单,Boost.Unordered 开址容器提供了非常有限的 API 来访问桶。

表 2. 访问桶的方法

所有容器

方法

描述

size_type bucket_count() const

桶的数量。

仅闭址容器

方法

描述

size_type max_bucket_count() const

桶数量的上限。

size_type bucket_size(size_type n) const

n 中的元素数量。

size_type bucket(key_type const& k) const

返回将包含k 的桶的索引。

local_iterator begin(size_type n)

返回桶n 的 begin 和 end 迭代器。

local_iterator end(size_type n)

const_local_iterator begin(size_type n) const

const_local_iterator end(size_type n) const

const_local_iterator cbegin(size_type n) const

const_local_iterator cend(size_type n) const

控制桶的数量

随着无序关联容器中添加的元素越来越多,冲突的数量也会增加,导致性能下降。为了解决这个问题,容器会在插入元素时增加桶的数量。您也可以通过调用rehash来告诉容器(如果需要)更改桶的数量。

标准为实现者决定桶的数量选择方式留下了很大的自由度,但它确实根据容器的负载因子(元素数量除以桶的数量)提出了一些要求。容器还具有一个最大负载因子,它们应该尽量保持负载因子低于此值。

您无法直接控制桶的数量,但有两种方法可以影响它

  • 在构造容器或调用rehash时指定最小桶数。

  • 通过调用max_load_factor建议最大负载因子。

max_load_factor并不能让您自己设置最大负载因子,它只是让您给出一个提示。即使那样,标准实际上也不要求容器过多地关注此值。负载因子*必须*小于最大值的情况只有在调用rehash之后。但是大多数实现都会尝试将元素数量保持在最大负载因子以下,并将最大负载因子设置为与提示相同或接近的值——除非您的提示不合理地小或大。

表3. 控制桶大小的方法

所有容器

方法

描述

X(size_type n)

构造一个至少具有n个桶的空容器(X是容器类型)。

X(InputIterator i, InputIterator j, size_type n)

构造一个至少具有n个桶的空容器,并插入范围[i, j)中的元素(X是容器类型)。

float load_factor() const

每个桶的平均元素数量。

float max_load_factor() const

返回当前最大负载因子。

float max_load_factor(float z)

使用z作为提示更改容器的最大负载因子。
**开放寻址和并发容器:**此函数不执行任何操作:不允许用户更改最大负载因子。

void rehash(size_type n)

更改桶的数量,以便至少有n个桶,并且负载因子小于最大负载因子。

仅限开放寻址和并发容器

方法

描述

size_type max_load() const

返回容器在重新哈希之前允许的最大元素数量。

关于开放寻址和并发容器的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。

表4 访问哈希和相等函数的方法
方法 描述

hasher hash_function() const

返回容器的哈希函数。

key_equal key_eq() const

返回容器的关键相等函数。

常规容器

Boost.Unordered闭址容器(boost::unordered_setboost::unordered_mapboost::unordered_multisetboost::unordered_multimap)完全符合C++无序关联容器规范,因此对于那些知道如何使用std::unordered_setstd::unordered_map等的使用者来说,它们在Boost.Unordered中的同名词可以直接替换。开放寻址容器(boost::unordered_node_setboost::unordered_node_mapboost::unordered_flat_setboost::unordered_flat_map)的接口非常相似,但它们存在一些在专用标准符合性部分中列出的细微差别。

对于没有使用哈希容器的经验但熟悉普通关联容器(std::setstd::mapstd::multisetstd::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

还有其他差异,这些差异列在与关联容器的比较部分。

迭代器失效

未指定除rehashreserve之外的其他成员函数如何影响桶计数,尽管只有当插入导致容器的负载大于最大允许负载时,insert才会使迭代器失效。对于大多数实现,这意味着insert只会在发生这种情况时才会更改桶的数量。迭代器可能会被对insertrehashreserve的调用使无效。

至于指针和引用,它们对于基于节点的容器(boost::unordered_[multi]setboost::unordered_[multi]mapboost::unordered_node_setboost::unordered_node_map)永远不会失效,但是当对boost::unordered_flat_setboost::unordered_flat_map进行重新哈希时,它们会失效:这是因为这些容器将元素直接存储到它们的持有桶中,因此在分配新的桶数组时,必须通过移动构造来转移元素。

与对vector使用reserve类似,在插入大量元素之前调用reserve可能是一个好主意。这将消除代价高昂的重新哈希操作,并让您可以安全地存储迭代器,确信它们不会失效。如果您要将n个元素插入容器x中,您可以首先调用

x.reserve(n);
注意

reserve(n)预留至少n个元素的空间,分配足够的桶以不超过最大负载因子。

因为最大负载因子定义为元素数量除以可用桶的总数,所以此函数在逻辑上等效于

x.rehash(std::ceil(n / x.max_load_factor()))

有关rehash函数的更多详细信息,请参阅参考

与关联容器的比较

表5 接口差异
关联容器 无序关联容器

由排序关系Compare参数化

由函数对象Hash和等价关系Pred参数化

可以使用成员函数key_comp()访问的key_compare比较键,可以使用成员函数value_comp()访问的value_compare比较值。

可以使用成员函数hash_function()访问的hasher对键进行哈希处理,并使用成员函数key_eq()访问的key_equal检查键的相等性。没有用于比较或哈希值的函数对象。

构造函数具有用于比较对象的可选额外参数。

构造函数具有用于初始最小桶数、哈希函数和相等对象的可选额外参数。

如果!Compare(k1, k2) && !Compare(k2, k1),则键k1k2被认为是等价的。

如果Pred(k1, k2),则键k1k2被认为是等价的。

成员函数lower_bound(k)upper_bound(k)

没有等价项。由于元素未排序,因此lower_boundupper_bound毫无意义。

equal_range(k)在如果k不存在于容器中,则返回k将被插入的位置处的空范围。

equal_range(k)如果k不存在于容器中,则返回容器末尾的范围。它不能返回定位范围,因为k可以插入到多个位置。
闭地址容器:要找出k将被插入的桶,请使用bucket(k)。但请记住,插入可能会导致容器重新哈希——这意味着元素可以被插入到不同的桶中。

iteratorconst_iterator属于双向迭代器类别。

iteratorconst_iterator至少属于前向迭代器类别。

容器元素的迭代器、指针和引用永远不会失效。

迭代器可能会因调用插入或重新哈希操作而失效。.
基于节点的容器:容器元素的指针和引用永远不会失效。
扁平容器:重新哈希发生时,容器元素的指针和引用将失效。

迭代器按照比较对象定义的顺序遍历容器。

迭代器以任意顺序遍历容器,该顺序可能会随着元素的插入而改变,尽管等效元素始终相邻。

无等效项

闭地址容器:可以使用局部迭代器遍历各个桶。(局部迭代器和迭代器的顺序不需要有任何对应关系。)

可以使用==!=<<=>>=运算符进行比较。

可以使用==!=运算符进行比较。

当使用提示进行插入时,允许实现忽略该提示。


表 6 复杂度保证
操作 关联容器 无序关联容器

空容器的构造

常数时间

O(n),其中n是最小桶数。

N个元素的范围构造容器

O(N log N),如果范围已按value_comp()排序,则为O(N)

平均情况 O(N),最坏情况 O(N2)

插入单个元素

对数时间

平均情况为常数时间,最坏情况为线性时间

使用提示插入单个元素

如果在提示之后立即插入t个元素,则为均摊常数时间;否则为对数时间。

平均情况为常数时间,最坏情况为线性时间(即与普通插入相同)。

插入N个元素的范围

N log(size() + N)

平均情况 O(N),最坏情况 O(N * size())

按键删除,k

O(log(size()) + count(k))

平均情况:O(count(k)),最坏情况:O(size())

按迭代器删除单个元素

均摊常数时间

平均情况:O(1),最坏情况:O(size())

删除N个元素的范围

O(log(size()) + N)

平均情况:O(N),最坏情况:O(size())

清空容器

O(size())

O(size())

查找

对数时间

平均情况:O(1),最坏情况:O(size())

计数

O(log(size()) + count(k))

平均情况:O(1),最坏情况:O(size())

equal_range(k)

对数时间

平均情况:O(count(k)),最坏情况:O(size())

lower_boundupper_bound

对数时间

n/a

并发容器

Boost.Unordered 提供了boost::concurrent_node_setboost::concurrent_node_mapboost::concurrent_flat_setboost::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_setboost::concurrent_node_mapboost::concurrent_flat_setboost::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_whileerase_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()时在插入期间发生。与非并发容器一样,预先为批量插入保留空间通常会加快处理速度。

与非并发容器的互操作性

由于开地址和并发容器基于相同的数据结构,因此可以从它们的非并发对应物高效地移动构造,反之亦然。

表 7. 并发/非并发互操作性

boost::concurrent_node_set

boost::unordered_node_set

boost::concurrent_node_map

boost::unordered_node_map

boost::concurrent_flat_set

boost::unordered_flat_set

boost::concurrent_flat_map

boost::unordered_flat_map

这种互操作性在多阶段场景中非常方便,在这些场景中,数据处理的部分工作并行进行,而其他步骤是非并发(或非修改)的。在下面的示例中,我们希望根据单词的巨大输入向量构建直方图:可以使用boost::concurrent_flat_map并行执行填充阶段,然后将结果传输到最终容器。

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]setboost::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_constructstd::tuple

交换

在交换时,目前不会通过调用swap来交换PredHash,而是使用它们的复制构造函数。因此,在交换时,它们的复制构造函数可能会抛出异常。

开址容器

C++ 标准目前没有提供任何开放寻址容器规范,因此boost::unordered_flat_set/unordered_node_setboost::unordered_flat_map/unordered_node_map分别从std::unordered_setstd::unordered_map中汲取灵感,并在方便时或根据其内部数据结构的要求偏离其接口,这与标准(闭合寻址)强加的数据结构有根本的不同。

Boost.Unordered 提供的开放寻址容器仅适用于相当兼容的 C++11(或更高版本)编译器。不会模拟诸如移动语义和可变参数模板参数之类的语言级功能。这些容器完全支持分配器,并支持高级指针

与 C++ 无序关联容器的主要区别在于

  • 一般来说

    • begin()不是常数时间。

    • erase(iterator)不会返回指向下一个元素的迭代器,而是一个代理对象,如果需要,该对象会转换为该迭代器;这避免了在不需要时进行潜在代价高昂的迭代器递增操作。

    • 没有用于桶处理的 API(bucket_count除外)。

    • 容器的最大负载因子是在内部管理的,用户无法设置。通过公共函数max_load公开的最大负载可能会在高负载条件下的擦除操作中减小。

  • 扁平容器 (boost::unordered_flat_setboost::unordered_flat_map)

    • value_type必须是可移动构造的。

    • 在重新哈希时,不会保持指针稳定性。

    • 没有用于节点提取/插入的 API。

并发容器

C++ 标准目前没有针对此类或任何其他类型的并发数据结构的规范。boost::concurrent_flat_set/boost::concurrent_node_setboost::concurrent_flat_map/boost::concurrent_node_map的 API 分别模拟了std::unordered_flat_setstd::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 编译器中,这些算法有并行版本。通常,并发容器的接口是通过一个相当简单的过程从其非并发对应物的接口派生的,该过程在适用时将迭代器替换为访问。如果对于常规映射,iteratorconst_iterator分别提供对元素的可变和常量访问,则此处访问会根据所用成员函数的常量性授予可变或常量访问(还有一些*cvisit重载用于显式常量访问);在boost::concurrent_flat_set的情况下,访问始终是常量。

boost::concurrent_flat_map/boost::concurrent_node_map没有提供的值得注意的操作是operator[]/at,如果以更复杂的方式,可以使用try_emplace_or_visit替换。

数据结构

闭址容器

Boost.Unordered 拥有闭合寻址(也通常称为分离链接)最快实现之一。下图显示了数据结构的示例。

bucket groups
图 1. 简单桶组方法

分配一个“桶”数组,每个桶又指向其自己的单独链表。这使得满足桶迭代的标准要求变得简单明了。不幸的是,使用此布局,整个容器的迭代通常很慢,因为必须检查每个桶的占用情况,当标准要求复杂度为O(size())时,其时间复杂度为O(bucket_count() + size())

规范的标准实现最终看起来像下面的图表:

singly linked
图 2. 规范的标准方法

值得注意的是,这种方法仅由 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
  // ...
}

在实践中,额外的间接寻址可能会对诸如insertfinderase之类的常见操作产生巨大的性能影响。但是,为了保持容器的快速迭代,Boost.Unordered 引入了一种新颖的数据结构,“桶组”。桶组是对桶数组子部分的固定宽度视图。它包含一个位掩码(一个std::size_t),用于跟踪桶的占用情况,并包含两个指针,以便它可以与非空组形成双链表。下图是一个示例图表。

fca
图 3. Boost 使用的新布局

因此,容器范围的迭代变成了遍历非空桶组(一个具有常数时间复杂度的操作),这将时间复杂度降低回O(size())。总的来说,桶组的大小只有 4 个字,它查看sizeof(std::size_t) * CHAR_BIT个桶,这意味着对于所有常见的实现,桶组只引入了每个桶 4 位的空间开销。

Boost.Unordered 的闭合寻址实现的更详细描述在一篇外部文章中给出。有关实现原理的更多信息,请阅读相应部分

开址容器

该图显示了boost::unordered_flat_set/unordered_node_setboost:unordered_flat_map/unordered_node_map的基本内部布局。

foa
图 4. Boost.Unordered 使用的开放寻址布局。

与所有开放寻址容器一样,元素(或者在boost::unordered_node_setboost::unordered_node_map的情况下为指向元素节点的指针)直接存储在桶数组中。该数组在逻辑上被划分为 2n,每个组包含 15 个元素。除了桶数组外,还有一个关联的元数据数组,其中包含 2n 个 16 字节的字。

foa metadata
图 5. 元数据字的分解。

元数据字被分成 15 个hi字节(每个关联的桶一个)和一个溢出字节(图中的ofw)。hi的值为:

  • 如果相应的桶为空,则为 0。

  • 1 用于编码一个称为哨兵的特殊空桶,该桶用于在完全遍历容器时在内部停止迭代。

  • 如果桶已占用,则为从元素的哈希值获得的简化哈希值

在查找哈希值为h的元素时,SIMD技术,例如SSE2Neon,允许我们非常快速地检查完整的元数据字,并在所有15个桶中查找h的简化值,只需少量CPU指令:不匹配的桶可以很容易地丢弃,而简化哈希值匹配的桶则需要通过与相应元素进行完整比较来检查。如果找不到要查找的元素,则检查溢出字节。

  • 如果位置h mod 8中的位为零,则查找终止(并且不存在该元素)。

  • 如果位设置为1(该组已溢出),则使用二次探测检查其他组,并重复此过程。

插入算法类似:使用SIMD查找空桶,当超过完整组时,其对应的溢出位设置为1。

在不支持SIMD的架构中,逻辑布局保持不变,但元数据字使用我们称之为位交错的技术进行编码:这种布局允许我们仅使用标准算术和逻辑运算就能以相当好的性能模拟SIMD。

foa metadata interleaving
图6. 位交错元数据字。

Boost.Unordered的开地址实现的更详细描述在外部文章中给出。有关实现原理的更多信息,请阅读相应章节

并发容器

boost::concurrent_flat_set/boost::concurrent_node_setboost::concurrent_flat_map/boost::concurrent_node_map使用上面描述的基本开地址布局,并增加了同步机制。

cfoa
图7. Boost.Unordered使用的并发开地址布局。

使用了两个级别的同步

  • 容器级别:读写互斥锁用于控制任何操作对容器的访问。通常,即使对于修改操作,此类访问也处于读取模式(即并发),因此对于大多数实际目的,此级别没有线程争用。只有在重新哈希或执行容器范围的操作(例如交换或赋值)时,访问才处于写入模式(阻塞)。

  • 组级别:每个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

插入
running insertion.xlsx.practice
running%20insertion.xlsx.practice non unique
running%20insertion.xlsx.practice non unique 5

非重复元素

重复元素

重复元素,
最大负载因子5

running%20insertion.xlsx.practice norehash
running%20insertion.xlsx.practice norehash non unique
running%20insertion.xlsx.practice norehash non unique 5

非重复元素,
之前的reserve

重复元素,
之前的reserve

重复元素,
最大负载因子5,
之前的reserve

擦除
scattered%20erasure.xlsx.practice
scattered%20erasure.xlsx.practice non unique
scattered%20erasure.xlsx.practice non unique 5

非重复元素

重复元素

重复元素,
最大负载因子5

scattered%20erasure%20by%20key.xlsx.practice non unique
scattered%20erasure%20by%20key.xlsx.practice non unique 5

按键,重复元素

按键,重复元素,
最大负载因子5

成功查找
scattered%20successful%20looukp.xlsx.practice
scattered%20successful%20looukp.xlsx.practice non unique
scattered%20successful%20looukp.xlsx.practice non unique 5

非重复元素

重复元素

重复元素,
最大负载因子5

不成功查找
scattered%20unsuccessful%20looukp.xlsx.practice
scattered%20unsuccessful%20looukp.xlsx.practice non unique
scattered%20unsuccessful%20looukp.xlsx.practice non unique 5

非重复元素

重复元素

重复元素,
最大负载因子5

Clang 15 + libc++,x64

插入
running%20insertion.xlsx.practice
running%20insertion.xlsx.practice non unique
running%20insertion.xlsx.practice non unique 5

非重复元素

重复元素

重复元素,
最大负载因子5

running%20insertion.xlsx.practice norehash
running%20insertion.xlsx.practice norehash non unique
running%20insertion.xlsx.practice norehash non unique 5

非重复元素,
之前的reserve

重复元素,
之前的reserve

重复元素,
最大负载因子5,
之前的reserve

擦除
scattered%20erasure.xlsx.practice
scattered%20erasure.xlsx.practice non unique
scattered%20erasure.xlsx.practice non unique 5

非重复元素

重复元素

重复元素,
最大负载因子5

scattered%20erasure%20by%20key.xlsx.practice non unique
scattered%20erasure%20by%20key.xlsx.practice non unique 5

按键,重复元素

按键,重复元素,
最大负载因子5

成功查找
scattered%20successful%20looukp.xlsx.practice
scattered%20successful%20looukp.xlsx.practice non unique
scattered%20successful%20looukp.xlsx.practice non unique 5

非重复元素

重复元素

重复元素,
最大负载因子5

不成功查找
scattered%20unsuccessful%20looukp.xlsx.practice
scattered%20unsuccessful%20looukp.xlsx.practice non unique
scattered%20unsuccessful%20looukp.xlsx.practice non unique 5

非重复元素

重复元素

重复元素,
最大负载因子5

Visual Studio 2022 + Dinkumware,x64

插入
running%20insertion.xlsx.practice
running%20insertion.xlsx.practice non unique
running%20insertion.xlsx.practice non unique 5

非重复元素

重复元素

重复元素,
最大负载因子5

running%20insertion.xlsx.practice norehash
running%20insertion.xlsx.practice norehash non unique
running%20insertion.xlsx.practice norehash non unique 5

非重复元素,
之前的reserve

重复元素,
之前的reserve

重复元素,
最大负载因子5,
之前的reserve

擦除
scattered%20erasure.xlsx.practice
scattered%20erasure.xlsx.practice non unique
scattered%20erasure.xlsx.practice non unique 5

非重复元素

重复元素

重复元素,
最大负载因子5

scattered%20erasure%20by%20key.xlsx.practice non unique
scattered%20erasure%20by%20key.xlsx.practice non unique 5

按键,重复元素

按键,重复元素,
最大负载因子5

成功查找
scattered%20successful%20looukp.xlsx.practice
scattered%20successful%20looukp.xlsx.practice non unique
scattered%20successful%20looukp.xlsx.practice non unique 5

非重复元素

重复元素

重复元素,
最大负载因子5

不成功查找
scattered%20unsuccessful%20looukp.xlsx.practice
scattered%20unsuccessful%20looukp.xlsx.practice non unique
scattered%20unsuccessful%20looukp.xlsx.practice non unique 5

非重复元素

重复元素

重复元素,
最大负载因子5

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

Running%20insertion.xlsx.plot
Running%20erasure.xlsx.plot
Scattered%20successful%20looukp.xlsx.plot
Scattered%20unsuccessful%20looukp.xlsx.plot

运行插入操作

运行擦除操作

查找成功

查找失败

Clang 15,x64

Running%20insertion.xlsx.plot
Running%20erasure.xlsx.plot
Scattered%20successful%20looukp.xlsx.plot
Scattered%20unsuccessful%20looukp.xlsx.plot

运行插入操作

运行擦除操作

查找成功

查找失败

Visual Studio 2022,x64

Running%20insertion.xlsx.plot
Running%20erasure.xlsx.plot
Scattered%20successful%20looukp.xlsx.plot
Scattered%20unsuccessful%20looukp.xlsx.plot

运行插入操作

运行擦除操作

查找成功

查找失败

Clang 12,ARM64

Running%20insertion.xlsx.plot
Running%20erasure.xlsx.plot
Scattered%20successful%20looukp.xlsx.plot
Scattered%20unsuccessful%20looukp.xlsx.plot

运行插入操作

运行擦除操作

查找成功

查找失败

GCC 12,x86

Running%20insertion.xlsx.plot
Running%20erasure.xlsx.plot
Scattered%20successful%20looukp.xlsx.plot
Scattered%20unsuccessful%20looukp.xlsx.plot

运行插入操作

运行擦除操作

查找成功

查找失败

Clang 15,x86

Running%20insertion.xlsx.plot
Running%20erasure.xlsx.plot
Scattered%20successful%20looukp.xlsx.plot
Scattered%20unsuccessful%20looukp.xlsx.plot

运行插入操作

运行擦除操作

查找成功

查找失败

Visual Studio 2022,x86

Running%20insertion.xlsx.plot
Running%20erasure.xlsx.plot
Scattered%20successful%20looukp.xlsx.plot
Scattered%20unsuccessful%20looukp.xlsx.plot

运行插入操作

运行擦除操作

查找成功

查找失败

boost::concurrent_(flat|node)_map

所有基准测试都是使用

源代码可以在这里找到

基准测试会使用多个线程 *T*(1 到 16 之间),这些线程并发执行在**更新**、**查找成功**和**查找失败**之间随机选择的运算。运算中使用的键遵循齐普夫定律,并具有不同的 *倾斜* 参数:倾斜度越高,键在覆盖范围的较低值中越集中。

boost::concurrent_flat_mapboost::concurrent_node_map 使用常规访问和批量访问进行测试:在后者的情况下,查找键会缓存在本地数组中,然后每次缓冲区达到bulk_visit_size 时一起处理。

GCC 12,x64

Parallel%20workload.xlsx.500k%2C%200.01
Parallel%20workload.xlsx.500k%2C%200.5
Parallel%20workload.xlsx.500k%2C%200.99

500k 次更新,450万次查找
倾斜度=0.01

500k 次更新,450万次查找
倾斜度=0.5

500k 次更新,450万次查找
倾斜度=0.99

Parallel%20workload.xlsx.5M%2C%200.01
Parallel%20workload.xlsx.5M%2C%200.5
Parallel%20workload.xlsx.5M%2C%200.99

500万次更新,4500万次查找
倾斜度=0.01

500万次更新,4500万次查找
倾斜度=0.5

500万次更新,4500万次查找
倾斜度=0.99

Clang 15,x64

Parallel%20workload.xlsx.500k%2C%200.01
Parallel%20workload.xlsx.500k%2C%200.5
Parallel%20workload.xlsx.500k%2C%200.99

500k 次更新,450万次查找
倾斜度=0.01

500k 次更新,450万次查找
倾斜度=0.5

500k 次更新,450万次查找
倾斜度=0.99

Parallel%20workload.xlsx.5M%2C%200.01
Parallel%20workload.xlsx.5M%2C%200.5
Parallel%20workload.xlsx.5M%2C%200.99

500万次更新,4500万次查找
倾斜度=0.01

500万次更新,4500万次查找
倾斜度=0.5

500万次更新,4500万次查找
倾斜度=0.99

Visual Studio 2022,x64

Parallel%20workload.xlsx.500k%2C%200.01
Parallel%20workload.xlsx.500k%2C%200.5
Parallel%20workload.xlsx.500k%2C%200.99

500k 次更新,450万次查找
倾斜度=0.01

500k 次更新,450万次查找
倾斜度=0.5

500k 次更新,450万次查找
倾斜度=0.99

Parallel%20workload.xlsx.5M%2C%200.01
Parallel%20workload.xlsx.5M%2C%200.5
Parallel%20workload.xlsx.5M%2C%200.99

500万次更新,4500万次查找
倾斜度=0.01

500万次更新,4500万次查找
倾斜度=0.5

500万次更新,4500万次查找
倾斜度=0.99

Clang 12,ARM64

Parallel%20workload.xlsx.500k%2C%200.01
Parallel%20workload.xlsx.500k%2C%200.5
Parallel%20workload.xlsx.500k%2C%200.99

500k 次更新,450万次查找
倾斜度=0.01

500k 次更新,450万次查找
倾斜度=0.5

500k 次更新,450万次查找
倾斜度=0.99

Parallel%20workload.xlsx.5M%2C%200.01
Parallel%20workload.xlsx.5M%2C%200.5
Parallel%20workload.xlsx.5M%2C%200.99

500万次更新,4500万次查找
倾斜度=0.01

500万次更新,4500万次查找
倾斜度=0.5

500万次更新,4500万次查找
倾斜度=0.99

GCC 12,x86

Parallel%20workload.xlsx.500k%2C%200.01
Parallel%20workload.xlsx.500k%2C%200.5
Parallel%20workload.xlsx.500k%2C%200.99

500k 次更新,450万次查找
倾斜度=0.01

500k 次更新,450万次查找
倾斜度=0.5

500k 次更新,450万次查找
倾斜度=0.99

Parallel%20workload.xlsx.5M%2C%200.01
Parallel%20workload.xlsx.5M%2C%200.5
Parallel%20workload.xlsx.5M%2C%200.99

500万次更新,4500万次查找
倾斜度=0.01

500万次更新,4500万次查找
倾斜度=0.5

500万次更新,4500万次查找
倾斜度=0.99

Clang 15,x86

Parallel%20workload.xlsx.500k%2C%200.01
Parallel%20workload.xlsx.500k%2C%200.5
Parallel%20workload.xlsx.500k%2C%200.99

500k 次更新,450万次查找
倾斜度=0.01

500k 次更新,450万次查找
倾斜度=0.5

500k 次更新,450万次查找
倾斜度=0.99

Parallel%20workload.xlsx.5M%2C%200.01
Parallel%20workload.xlsx.5M%2C%200.5
Parallel%20workload.xlsx.5M%2C%200.99

500万次更新,4500万次查找
倾斜度=0.01

500万次更新,4500万次查找
倾斜度=0.5

500万次更新,4500万次查找
倾斜度=0.99

Visual Studio 2022,x86

Parallel%20workload.xlsx.500k%2C%200.01
Parallel%20workload.xlsx.500k%2C%200.5
Parallel%20workload.xlsx.500k%2C%200.99

500k 次更新,450万次查找
倾斜度=0.01

500k 次更新,450万次查找
倾斜度=0.5

500k 次更新,450万次查找
倾斜度=0.99

Parallel%20workload.xlsx.5M%2C%200.01
Parallel%20workload.xlsx.5M%2C%200.5
Parallel%20workload.xlsx.5M%2C%200.99

500万次更新,4500万次查找
倾斜度=0.01

500万次更新,4500万次查找
倾斜度=0.5

500万次更新,4500万次查找
倾斜度=0.99

实现原理

闭址容器

boost::unordered_[multi]setboost::unordered_[multi]map 遵守无序关联容器的标准要求,因此接口是固定的。但是仍然有一些实现决策需要做出。优先级是符合标准和可移植性。

关于哈希表的维基百科文章对一般哈希表的实现问题进行了很好的总结。

数据结构

通过指定访问容器桶的接口,标准几乎要求哈希表使用闭地址法。

可以想象编写一个使用其他方法的哈希表。例如,它可以使用开地址法,并使用查找链充当桶,但这样做会有一些严重的问题。

  • 标准要求元素的指针不会失效,因此元素不能存储在一个数组中,而需要一个间接层——从而损失了效率和大部分内存增益,这是开地址法的主要优点。

  • 局部迭代器效率会非常低,并且可能无法满足复杂性要求。

  • 迭代器失效时间的限制也是如此。由于开地址法在发生大量冲突时性能会严重下降,因此这些限制可能会阻止在真正需要时进行重新哈希。最大负载因子可以设置为较低的值来解决此问题——但是标准要求将其初始设置为 1.0。

  • 而且,由于标准是针对闭地址法编写的,如果性能没有反映这一点,用户会感到惊讶。

因此,使用闭地址法。

桶的数量

有两种常用的方法来选择哈希表中桶的数量。一种是使用素数个桶,另一种是使用 2 的幂。

使用素数个桶,并通过使用哈希函数结果的模来选择桶通常会得到一个好的结果。缺点是所需的模运算相当昂贵。这在大多数情况下是容器过去所做的。

使用 2 的幂允许更快地选择要使用的桶,但代价是丢失哈希值的高位。对于某些专门设计的哈希函数,可以这样做并仍然获得良好的结果,但由于容器可以采用任意哈希函数,因此无法依赖此方法。

为了避免这种情况,可以对哈希函数应用转换,例如参见Thomas Wang 关于整数哈希函数的文章。不幸的是,像 Wang 的转换那样需要知道哈希值中的位数,因此仅在 size_t 为 64 位时才使用。

从 1.79.0 版本开始,改为使用斐波那契哈希。在此实现中,桶号通过使用 (h * m) >> (w - k) 确定,其中 h 是哈希值,m2^w 除以黄金比例,w 是字大小(32 或 64),2^k 是桶的数量。这在速度和分布之间提供了良好的折衷。

从 1.80.0 版本开始,素数与复杂的模运算一起用于选择桶的数量。这消除了对用户哈希函数结果的“混合”的需求,而 1.79.0 版本使用了这种方法。

开址容器

C++ 标准对无序关联容器的规范对允许的实现施加了严格的限制,其中最重要的是隐式假设了闭地址法。稍微放宽此规范可以打开提供容器变体的可能性,这些变体可以充分利用开地址法技术。

boost::unordered_flat_set/unordered_node_setboost::unordered_flat_map/unordered_node_map 的设计遵循 Peter Dimov 的Boost.Unordered 开发计划。我们在此讨论最相关的原则。

哈希函数

鉴于其丰富的功能和跨平台互操作性,boost::hash 仍然是开地址容器的默认哈希函数。碰巧的是,对于整数和其他基本类型,boost::hash 不具备开地址法所需的统计特性;为了应对这个问题,我们实现了一个后混合阶段

     ah mulx C,
     hhigh(a) xor low(a),

其中 mulx扩展乘法(在 64 位架构中为 128 位,在 32 位环境中为 64 位),highlow 分别是扩展字的上半部分和下半部分。在 64 位架构中,C 是 264φ 的整数部分,而在 32 位中,C = 0xE817FB2Du 是从Steele 和 Vigna (2021)获得的。

当使用直接适合开地址法的哈希函数时,可以通过专用的hash_is_avalanching 特性选择退出后混合。字符串类型的 boost::hash 特化被标记为 avalanching(雪崩)。

平台互操作性

只要它们的 std::size_t 大小相同,并且用户提供的哈希函数和相等谓词也具有互操作性,boost::unordered_flat_set/unordered_node_setboost::unordered_flat_map/unordered_node_map 的可观察行为在不同的编译器上确定性地相同——这包括对于相同的操作序列,元素以完全相同的方式排序。

尽管实现内部使用了 SIMD 技术,例如SSE2Neon(如果可用),但这不会影响互操作性。例如,在具有 SSE2 的 x64 模式 Intel CPU 上的 Visual Studio 和在没有任何支持的 SIMD 技术的 IBM s390x 上的 GCC 的行为相同。

并发容器

Boost.Unordered 开地址容器使用的相同数据结构也被选为boost::concurrent_flat_set/boost::concurrent_node_setboost::concurrent_flat_map/boost::concurrent_node_map 的基础。

  • 开地址法比闭地址法替代方案更快,无论是在非并发场景还是并发场景中。

  • 开地址布局非常适合并发访问和修改,并且锁的开销最小。特别是,元数据数组可用于实现查找,直到实际元素比较的最后一步都是无锁的。

  • 与 Boost.Unordered flat 容器的布局兼容性允许在并发容器及其非并发对应物之间快速传输所有元素,反之亦然。

哈希函数和平台互操作性

关于哈希函数默认值平台互操作性,并发容器做出的决策和提供的保证与 Boost.Unordered 开地址容器相同。

参考

类模板 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>>>;
  }
}

描述

模板参数

Key 必须能够从容器中擦除(即 allocator_traits 可以销毁它)。

T

T 必须能够从容器中擦除(即 allocator_traits 可以销毁它)。

哈希

一个一元函数对象类型,充当 Key 的哈希函数。它接受一个类型为 Key 的参数,并返回一个类型为 std::size_t 的值。

Pred

一个二元函数对象,它在 Key 类型的值上实现等价关系。一个二元函数对象,它在 Key 类型的值上引入等价关系。它接受两个类型为 Key 的参数,并返回一个布尔值。

分配器

一个分配器,其值类型与容器的值类型相同。支持使用花哨指针的分配器。

元素被组织成桶。具有相同哈希码的键存储在同一个桶中。

桶的数量可以通过调用插入操作自动增加,或者作为调用重新哈希的结果。

配置宏

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 = iteratorNodeType = node_type


构造函数

默认构造函数
unordered_map();

使用 hasher() 作为哈希函数、key_equal() 作为键相等谓词、allocator_type() 作为分配器和 1.0 的最大负载因子来构造一个空容器。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


桶计数构造函数
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` 个桶的空容器。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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());

使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `[f, l)` 区间内的元素插入其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


复制构造函数
unordered_map(unordered_map const& other);

复制构造函数。复制包含的元素、哈希函数、谓词、最大负载因子和分配器。

如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。

要求

`value_type` 可复制构造。


移动构造函数
unordered_map(unordered_map&& other);

移动构造函数。

注释

这是使用 Boost.Move 实现的。

要求

`value_type` 可移动构造。


带分配器的迭代器范围构造函数
template<class InputIterator>
  unordered_map(InputIterator f, InputIterator l, const allocator_type& a);

使用 `a` 作为分配器,使用默认的哈希函数和键相等谓词,最大负载因子为 `1.0`,构造一个空容器,并将 `[f, l)` 区间内的元素插入其中。

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


分配器构造函数
explicit unordered_map(Allocator const& a);

使用分配器 `a` 构造一个空容器。


带分配器的复制构造函数
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` 中的元素插入其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


带分配器的桶计数构造函数
unordered_map(size_type n, allocator_type const& a);

使用 `hf` 作为哈希函数,使用默认的哈希函数和键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。

后置条件

size() == 0

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


带哈希函数和分配器的桶计数构造函数
unordered_map(size_type n, hasher const& hf, allocator_type const& a);

使用 `hf` 作为哈希函数,使用默认的键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。

后置条件

size() == 0

要求

`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();
注意

析构函数将应用于每个元素,并且所有内存都将被释放。


赋值

复制赋值
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` 可移动构造。


初始化列表赋值
unordered_map& operator=(std::initializer_list<value_type> il);

从初始化列表中的值赋值。所有现有元素要么被新元素覆盖,要么被销毁。

要求

`value_type` 可 复制插入 到容器中,并且可 复制赋值

迭代器

begin
iterator begin() noexcept;
const_iterator begin() const noexcept;
返回值

指向容器第一个元素的迭代器,如果容器为空,则返回容器的尾后值。


end
iterator end() noexcept;
const_iterator end() const noexcept;
返回值

指向容器尾后值的迭代器。


cbegin
const_iterator cbegin() const noexcept;
返回值

指向容器第一个元素的 `const_iterator`,如果容器为空,则返回容器的尾后值。


cend
const_iterator cend() const noexcept;
返回值

指向容器尾后值的 `const_iterator`。


大小和容量

empty
[[nodiscard]] bool empty() const noexcept;
返回值

size() == 0


size
size_type size() const noexcept;
返回值

std::distance(begin(), end())


max_size
size_type max_size() const noexcept;
返回值

最大可能容器的 `size()`。


修改器

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` 的构造使用

// first two overloads
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<Key>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

// third overload
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<K>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

而不是 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` 的构造使用

// first two overloads
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<Key>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

// third overload
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<K>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

而不是 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的元素。

返回值

如果找到,则返回拥有该元素的node_type,否则返回空的node_type

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

使用此方法提取的节点可以插入到兼容的 `unordered_multimap` 中。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义,并且iteratorconst_iterator都不能隐式转换为K时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


使用node_handle插入
insert_return_type insert(node_type&& nh);

如果nh为空,则没有效果。

否则,当且仅当容器中没有具有等效键的元素时,插入nh拥有的元素。

要求

nh为空或nh.get_allocator()等于容器的分配器。

返回值

如果nh为空,则返回一个insert_return_type,其中:inserted等于falseposition等于end()node为空。

否则,如果已经存在具有等效键的元素,则返回一个insert_return_type,其中:inserted等于falseposition指向匹配的元素,node包含来自nh的节点。

否则,如果插入成功,则返回一个insert_return_type,其中:inserted等于trueposition指向新插入的元素,node为空。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。

指向元素的指针和引用永远不会失效。

这可以用来插入从兼容的unordered_multimap中提取的节点。


使用提示和node_handle插入
iterator insert(const_iterator hint, node_type&& nh);

如果nh为空,则没有效果。

否则,当且仅当容器中没有具有等效键的元素时,插入nh拥有的元素。

如果容器中已经存在具有等效键的元素,则对nh没有影响(即nh仍然包含该节点)。

`hint` 是关于应插入元素位置的建议。

要求

nh为空或nh.get_allocator()等于容器的分配器。

返回值

如果nh为空,则返回end()

如果容器中已经存在具有等效键的元素,则返回指向该元素的迭代器。

否则,返回指向新插入元素的迭代器。

抛出

如果由除对hasher的调用以外的操作抛出异常,则该函数无效。

注释

标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。

可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。

指向元素的指针和引用永远不会失效。

这可以用来插入从兼容的unordered_multimap中提取的节点。


按位置删除
iterator erase(iterator position);
iterator erase(const_iterator position);

删除position指向的元素。

返回值

删除之前position之后的迭代器。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

在旧版本中,这可能效率低下,因为它必须搜索多个桶才能找到返回迭代器的位置。数据结构已更改,因此这种情况不再发生,并且替代删除方法已被弃用。


按键删除
size_type erase(const key_type& k);
template<class K> size_type erase(K&& k);

删除所有键值等效于k的元素。

返回值

删除的元素数量。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义,并且iteratorconst_iterator都不能隐式转换为K时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


删除范围
iterator erase(const_iterator first, const_iterator last);

删除从firstlast范围内的元素。

返回值

删除元素之后的迭代器 - 即last

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。


快速删除(quick_erase)
void quick_erase(const_iterator position);

删除position指向的元素。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。

注释

实现此方法是因为从erase返回指向下一个元素的迭代器代价很高,但是容器已经重新设计,这种情况不再发生。因此,此方法现已弃用。


返回void的删除 (erase_return_void)
void erase_return_void(const_iterator position);

删除position指向的元素。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。

注释

实现此方法是因为从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::valuetrue,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。

抛出

除非key_equalhasher的复制构造函数或复制赋值运算符抛出异常,否则不会抛出异常。

注释

异常规范与C++11标准略有不同,因为相等谓词和哈希函数使用它们的复制构造函数进行交换。


清空 (clear)
void clear();

删除容器中的所有元素。

后置条件

size() == 0

抛出

从不抛出异常。


合并 (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

注释
  • 指向已转移元素的指针和引用保持有效。

  • 使指向已转移元素的迭代器失效。

  • 使属于*this的迭代器失效。

  • 指向source中未转移元素的迭代器保持有效。


观察者

获取分配器 (get_allocator)
allocator_type get_allocator() const;

哈希函数 (hash_function)
hasher hash_function() const;
返回值

容器的哈希函数。


键相等谓词 (key_eq)
key_equal key_eq() const;
返回值

容器的键相等谓词。


查找

查找 (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;
返回值

指向键值等效于k的元素的迭代器,如果不存在这样的元素,则为b.end()

注释

包含CompatibleKeyCompatibleHashCompatiblePredicate的模板重载是非标准扩展,允许你使用不同类型的键的兼容哈希函数和相等谓词,以避免代价高昂的类型转换。一般来说,不鼓励使用它,而应该使用K成员函数模板。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


计数 (count)
size_type        count(const key_type& k) const;
template<class K>
  size_type      count(const K& k) const;
返回值

键值等效于k的元素数量。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


包含 (contains)
bool             contains(const key_type& k) const;
template<class K>
  bool           contains(const K& k) const;
返回值

一个布尔值,指示容器中是否存在键等于key的元素。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


相等范围 (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;
返回值

包含所有键值等效于k的元素的范围。如果容器不包含任何此类元素,则返回std::make_pair(b.end(), b.end())

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


下标运算符 (operator[])
mapped_type& operator[](const key_type& k);
mapped_type& operator[](key_type&& k);
template<class K> mapped_type& operator[](K&& k);
效果

如果容器还不包含键值等效于k的元素,则插入值std::pair<key_type const, mapped_type>(k, mapped_type())

返回值

x.second的引用,其中x是容器中已存在的元素,或新插入的键值等效于k的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。

指向元素的指针和引用永远不会失效。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


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;
返回值

x.second的引用,其中x是键值等效于k的(唯一)元素。

抛出

如果不存在这样的元素,则抛出类型为std::out_of_range的异常对象。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


桶接口

桶数量 (bucket_count)
size_type bucket_count() const noexcept;
返回值

桶的数量。


最大桶数量 (max_bucket_count)
size_type max_bucket_count() const noexcept;
返回值

桶数量的上限。


桶大小 (bucket_size)
size_type bucket_size(size_type n) const;
要求

n < bucket_count()

返回值

n 中的元素数量。


桶 (bucket)
size_type bucket(const key_type& k) const;
template<class K> size_type bucket(const K& k) const;
返回值

将包含键为k的元素的桶的索引。

后置条件

返回值小于bucket_count()

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


begin
local_iterator begin(size_type n);
const_local_iterator begin(size_type n) const;
要求

n应在范围[0, bucket_count())内。

返回值

指向索引为n的桶中第一个元素的局部迭代器。


end
local_iterator end(size_type n);
const_local_iterator end(size_type n) const;
要求

n应在范围[0, bucket_count())内。

返回值

指向索引为n的桶中“最后一个元素之后”的局部迭代器。


cbegin
const_local_iterator cbegin(size_type n) const;
要求

n应在范围[0, bucket_count())内。

返回值

指向索引为n的桶中第一个元素的常量局部迭代器。


cend
const_local_iterator cend(size_type n) const;
要求

n应在范围[0, bucket_count())内。

返回值

指向索引为n的桶中“最后一个元素之后”的常量局部迭代器。


哈希策略

负载因子 (load_factor)
float load_factor() const noexcept;
返回值

每个桶的平均元素数量。


最大负载因子 (max_load_factor)
float max_load_factor() const noexcept;
返回值

返回当前最大负载因子。


设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果

使用z作为提示更改容器的最大负载因子。


重新哈希 (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)));

交换xy的内容。

如果声明了Allocator::propagate_on_container_swap并且Allocator::propagate_on_container_swap::valuetrue,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。

效果

x.swap(y)

抛出

除非key_equalhasher的复制构造函数或复制赋值运算符抛出异常,否则不会抛出异常。

注释

异常规范与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的元素。

返回值

被移除元素的数量。

注释

等价于

auto original_size = c.size();
for (auto i = c.begin(), last = c.end(); i != last; ) {
  if (pred(*i)) {
    i = c.erase(i);
  } else {
    ++i;
  }
}
return original_size - c.size();

序列化

可以使用此库提供的API通过Boost.Serialization存档/检索unordered_map。支持常规存档和XML存档。

将unordered_map保存到存档

unordered_map x的所有元素保存到存档(XML存档)ar中。

要求

std::remove_const<key_type>::typestd::remove_const<mapped_type>::type是可序列化的(XML可序列化的),并且它们支持Boost.Serialization save_construct_data/load_construct_data协议(由DefaultConstructible类型自动支持)。


从存档加载unordered_map

删除unordered_map x中所有预先存在的元素,并从存档(XML存档)ar中插入原始unordered_map other保存到ar读取的存储中的元素的已恢复副本。

要求

value_type可以从(std::remove_const<key_type>::type&&, std::remove_const<mapped_type>::type&&)EmplaceConstructible构建。x.key_equal()的功能等同于other.key_equal()

注意

如果存档是使用Boost 1.84之前的Boost版本保存的,则必须全局定义配置宏BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0才能使此操作成功;否则,将抛出异常。


将迭代器/const_iterator保存到存档

iteratorconst_iteratorit的位置信息保存到存档(XML存档)ar中。it可以是end()迭代器。

要求

it指向的unordered_map x之前已保存到ar中,并且在保存x和保存it之间没有对x进行修改操作。


从存档加载迭代器/const_iterator

使iteratorconst_iteratorit指向原始iteratorconst_iterator)在存档(XML存档)ar读取的存储中保存的已恢复位置。

要求

如果xit指向的unordered_map,则在加载x和加载it之间没有对x进行修改操作。

类模板 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>>>;
  }
}

描述

模板参数

Key 必须能够从容器中擦除(即 allocator_traits 可以销毁它)。

T

T 必须能够从容器中擦除(即 allocator_traits 可以销毁它)。

哈希

一个一元函数对象类型,充当 Key 的哈希函数。它接受一个类型为 Key 的参数,并返回一个类型为 std::size_t 的值。

Pred

一个二元函数对象,它在 Key 类型的值上实现等价关系。一个二元函数对象,它在 Key 类型的值上引入等价关系。它接受两个类型为 Key 的参数,并返回一个布尔值。

分配器

一个分配器,其值类型与容器的值类型相同。支持使用花哨指针的分配器。

元素被组织成桶。具有相同哈希码的键存储在同一个桶中。

桶的数量可以通过调用插入操作自动增加,或者作为调用重新哈希的结果。

配置宏

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 的最大负载因子来构造一个空容器。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


桶计数构造函数
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` 个桶的空容器。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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());

使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `[f, l)` 区间内的元素插入其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


复制构造函数
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` 需要是 可默认构造的


分配器构造函数
explicit unordered_multimap(const Allocator& a);

使用分配器 `a` 构造一个空容器。


带分配器的复制构造函数
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` 中的元素插入其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


带分配器的桶计数构造函数
unordered_multimap(size_type n, const allocator_type& a);

使用 `hf` 作为哈希函数,使用默认的哈希函数和键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。

后置条件

size() == 0

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


带哈希函数和分配器的桶计数构造函数
unordered_multimap(size_type n, const hasher& hf, const allocator_type& a);

使用 `hf` 作为哈希函数,使用默认的键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。

后置条件

size() == 0

要求

`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();
注意

析构函数将应用于每个元素,并且所有内存都将被释放。


赋值

复制赋值
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` 可移动构造。


初始化列表赋值
unordered_multimap& operator=(std::initializer_list<value_type> il);

从初始化列表中的值赋值。所有现有元素要么被新元素覆盖,要么被销毁。

要求

`value_type` 可 复制插入 到容器中,并且可 复制赋值

迭代器

begin
iterator begin() noexcept;
const_iterator begin() const noexcept;
返回值

指向容器第一个元素的迭代器,如果容器为空,则返回容器的尾后值。


end
iterator       end() noexcept;
const_iterator end() const noexcept;
返回值

指向容器尾后值的迭代器。


cbegin
const_iterator cbegin() const noexcept;
返回值

指向容器第一个元素的 `const_iterator`,如果容器为空,则返回容器的尾后值。


cend
const_iterator cend() const noexcept;
返回值

指向容器尾后值的 `const_iterator`。


大小和容量

empty
[[nodiscard]] bool empty() const noexcept;
返回值

size() == 0


size
size_type size() const noexcept;
返回值

std::distance(begin(), end())


max_size
size_type max_size() const noexcept;
返回值

最大可能容器的 `size()`。


修改器

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`。

注释

使用此方法提取的节点可以插入到兼容的unordered_map中。


通过键提取
node_type extract(const key_type& k);
template<class K> node_type extract(K&& k);

移除键值等效于k的元素。

返回值

如果找到,则返回拥有该元素的node_type,否则返回空的node_type

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

使用此方法提取的节点可以插入到兼容的unordered_map中。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义,并且iteratorconst_iterator都不能隐式转换为K时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


使用node_handle插入
iterator insert(node_type&& nh);

如果nh为空,则没有效果。

否则插入nh拥有的元素。

要求

nh为空或nh.get_allocator()等于容器的分配器。

返回值

如果nh为空,则返回end()

否则,返回指向新插入元素的迭代器。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。

指向元素的指针和引用永远不会失效。

这可以用来插入从兼容的unordered_map中提取的节点。


使用提示和node_handle插入
iterator insert(const_iterator hint, node_type&& nh);

如果nh为空,则没有效果。

否则插入nh拥有的元素。

`hint` 是关于应插入元素位置的建议。

要求

nh为空或nh.get_allocator()等于容器的分配器。

返回值

如果nh为空,则返回end()

否则,返回指向新插入元素的迭代器。

抛出

如果由除对hasher的调用以外的操作抛出异常,则该函数无效。

注释

标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。

可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。

指向元素的指针和引用永远不会失效。

这可以用来插入从兼容的unordered_map中提取的节点。


按位置删除
iterator erase(iterator position);
iterator erase(const_iterator position);

删除position指向的元素。

返回值

删除之前position之后的迭代器。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

在旧版本中,这可能效率低下,因为它必须搜索多个桶才能找到返回迭代器的位置。数据结构已更改,因此这种情况不再发生,并且替代删除方法已被弃用。


按键删除
size_type erase(const key_type& k);
template<class K> size_type erase(K&& k);

删除所有键值等效于k的元素。

返回值

删除的元素数量。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义,并且iteratorconst_iterator都不能隐式转换为K时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


删除范围
iterator erase(const_iterator first, const_iterator last);

删除从firstlast范围内的元素。

返回值

删除元素之后的迭代器 - 即last

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。


快速删除(quick_erase)
void quick_erase(const_iterator position);

删除position指向的元素。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。

注释

实现此方法是因为从erase返回指向下一个元素的迭代器代价很高,但是容器已经重新设计,这种情况不再发生。因此,此方法现已弃用。


返回void的删除 (erase_return_void)
void erase_return_void(const_iterator position);

删除position指向的元素。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。

注释

实现此方法是因为从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::valuetrue,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。

抛出

除非key_equalhasher的复制构造函数或复制赋值运算符抛出异常,否则不会抛出异常。

注释

异常规范与C++11标准略有不同,因为相等谓词和哈希函数使用它们的复制构造函数进行交换。


清空 (clear)
void clear() noexcept;

删除容器中的所有元素。

后置条件

size() == 0

抛出

从不抛出异常。


合并 (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

注释
  • 指向已转移元素的指针和引用保持有效。

  • 使指向已转移元素的迭代器失效。

  • 使属于*this的迭代器失效。

  • 指向source中未转移元素的迭代器保持有效。


观察者

获取分配器 (get_allocator)
allocator_type get_allocator() const;

哈希函数 (hash_function)
hasher hash_function() const;
返回值

容器的哈希函数。


键相等谓词 (key_eq)
key_equal key_eq() const;
返回值

容器的键相等谓词。


查找

查找 (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;
返回值

指向键值等效于k的元素的迭代器,如果不存在这样的元素,则为b.end()

注释

包含CompatibleKeyCompatibleHashCompatiblePredicate的模板重载是非标准扩展,允许你使用不同类型的键的兼容哈希函数和相等谓词,以避免代价高昂的类型转换。一般来说,不鼓励使用它,而应该使用K成员函数模板。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


计数 (count)
size_type        count(const key_type& k) const;
template<class K>
  size_type      count(const K& k) const;
返回值

键值等效于k的元素数量。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


包含 (contains)
bool             contains(const key_type& k) const;
template<class K>
  bool           contains(const K& k) const;
返回值

一个布尔值,指示容器中是否存在键等于key的元素。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


相等范围 (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;
返回值

包含所有键值等效于k的元素的范围。如果容器不包含任何此类元素,则返回std::make_pair(b.end(), b.end())

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


桶接口

桶数量 (bucket_count)
size_type bucket_count() const noexcept;
返回值

桶的数量。


最大桶数量 (max_bucket_count)
size_type max_bucket_count() const noexcept;
返回值

桶数量的上限。


桶大小 (bucket_size)
size_type bucket_size(size_type n) const;
要求

n < bucket_count()

返回值

n 中的元素数量。


桶 (bucket)
size_type bucket(const key_type& k) const;
template<class K> size_type bucket(const K& k) const;
返回值

将包含键为k的元素的桶的索引。

后置条件

返回值小于bucket_count()

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


begin
local_iterator begin(size_type n);
const_local_iterator begin(size_type n) const;
要求

n应在范围[0, bucket_count())内。

返回值

指向索引为n的桶中第一个元素的局部迭代器。


end
local_iterator end(size_type n);
const_local_iterator end(size_type n) const;
要求

n应在范围[0, bucket_count())内。

返回值

指向索引为n的桶中“最后一个元素之后”的局部迭代器。


cbegin
const_local_iterator cbegin(size_type n) const;
要求

n应在范围[0, bucket_count())内。

返回值

指向索引为n的桶中第一个元素的常量局部迭代器。


cend
const_local_iterator cend(size_type n) const;
要求

n应在范围[0, bucket_count())内。

返回值

指向索引为n的桶中“最后一个元素之后”的常量局部迭代器。


哈希策略

负载因子 (load_factor)
float load_factor() const noexcept;
返回值

每个桶的平均元素数量。


最大负载因子 (max_load_factor)
float max_load_factor() const noexcept;
返回值

返回当前最大负载因子。


设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果

使用z作为提示更改容器的最大负载因子。


重新哈希 (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)));

交换xy的内容。

如果声明了Allocator::propagate_on_container_swap并且Allocator::propagate_on_container_swap::valuetrue,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。

效果

x.swap(y)

抛出

除非key_equalhasher的复制构造函数或复制赋值运算符抛出异常,否则不会抛出异常。

注释

异常规范与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的元素。

返回值

被移除元素的数量。

注释

等价于

auto original_size = c.size();
for (auto i = c.begin(), last = c.end(); i != last; ) {
  if (pred(*i)) {
    i = c.erase(i);
  } else {
    ++i;
  }
}
return original_size - c.size();

序列化

可以使用此库提供的API通过Boost.Serialization存档/检索unordered_multimap。支持常规存档和XML存档。

将unordered_multimap保存到存档

unordered_multimap x的所有元素保存到存档(XML存档)ar中。

要求

std::remove_const<key_type>::typestd::remove_const<mapped_type>::type是可序列化的(XML可序列化的),并且它们支持Boost.Serialization save_construct_data/load_construct_data协议(由DefaultConstructible类型自动支持)。


从存档加载unordered_multimap

删除unordered_multimap x中所有预先存在的元素,并从存档(XML存档)ar中插入原始unordered_multimap other保存到ar读取的存储中的元素的已恢复副本。

要求

value_type可以从(std::remove_const<key_type>::type&&, std::remove_const<mapped_type>::type&&)EmplaceConstructible构建。x.key_equal()的功能等同于other.key_equal()

注意

如果存档是使用Boost 1.84之前的Boost版本保存的,则必须全局定义配置宏BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0才能使此操作成功;否则,将抛出异常。


将迭代器/const_iterator保存到存档

iteratorconst_iteratorit的位置信息保存到存档(XML存档)ar中。it可以是end()迭代器。

要求

it指向的unordered_multimap x之前已保存到ar中,并且在保存x和保存it之间没有对x进行修改操作。


从存档加载迭代器/const_iterator

使iteratorconst_iteratorit指向原始iteratorconst_iterator)在存档(XML存档)ar读取的存储中保存的已恢复位置。

要求

如果xit指向的unordered_multimap,则在加载x和加载it之间没有对x进行修改操作。

类模板 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>>;
  }
}

描述

模板参数

Key 必须能够从容器中擦除(即 allocator_traits 可以销毁它)。

哈希

一个一元函数对象类型,充当 Key 的哈希函数。它接受一个类型为 Key 的参数,并返回一个类型为 std::size_t 的值。

Pred

一个二元函数对象,它在 Key 类型的值上实现等价关系。一个二元函数对象,它在 Key 类型的值上引入等价关系。它接受两个类型为 Key 的参数,并返回一个布尔值。

分配器

一个分配器,其值类型与容器的值类型相同。支持使用花哨指针的分配器。

元素被组织成桶。具有相同哈希码的键存储在同一个桶中。

桶的数量可以通过调用插入操作自动增加,或者作为调用重新哈希的结果。

配置宏

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 = iteratorNodeType = node_type


构造函数

默认构造函数
unordered_set();

使用 hasher() 作为哈希函数、key_equal() 作为键相等谓词、allocator_type() 作为分配器和 1.0 的最大负载因子来构造一个空容器。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


桶计数构造函数
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` 个桶的空容器。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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());

使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `[f, l)` 区间内的元素插入其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


复制构造函数
unordered_set(const unordered_set& other);

复制构造函数。复制包含的元素、哈希函数、谓词、最大负载因子和分配器。

如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。

要求

`value_type` 可复制构造。


移动构造函数
unordered_set(unordered_set&& other);

移动构造函数。

注释

这是使用 Boost.Move 实现的。

要求

`value_type` 可移动构造。


带分配器的迭代器范围构造函数
template<class InputIterator>
  unordered_set(InputIterator f, InputIterator l, const allocator_type& a);

使用 `a` 作为分配器,使用默认的哈希函数和键相等谓词,最大负载因子为 `1.0`,构造一个空容器,并将 `[f, l)` 区间内的元素插入其中。

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


分配器构造函数
explicit unordered_set(const Allocator& a);

使用分配器 `a` 构造一个空容器。


带分配器的复制构造函数
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` 中的元素插入其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


带分配器的桶计数构造函数
unordered_set(size_type n, const allocator_type& a);

使用 `hf` 作为哈希函数,使用默认的哈希函数和键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。

后置条件

size() == 0

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


带哈希函数和分配器的桶计数构造函数
unordered_set(size_type n, const hasher& hf, const allocator_type& a);

使用 `hf` 作为哈希函数,使用默认的键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。

后置条件

size() == 0

要求

`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();
注意

析构函数将应用于每个元素,并且所有内存都将被释放。


赋值

复制赋值
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` 可移动构造。


初始化列表赋值
unordered_set& operator=(std::initializer_list<value_type> il);

从初始化列表中的值赋值。所有现有元素要么被新元素覆盖,要么被销毁。

要求

`value_type` 可 复制插入 到容器中,并且可 复制赋值


迭代器

begin
iterator       begin() noexcept;
const_iterator begin() const noexcept;
返回值

指向容器第一个元素的迭代器,如果容器为空,则返回容器的尾后值。


end
iterator       end() noexcept;
const_iterator end() const noexcept;
返回值

指向容器尾后值的迭代器。


cbegin
const_iterator cbegin() const noexcept;
返回值

指向容器第一个元素的 `const_iterator`,如果容器为空,则返回容器的尾后值。


cend
const_iterator cend() const noexcept;
返回值

指向容器尾后值的 `const_iterator`。


大小和容量

empty
[[nodiscard]] bool empty() const noexcept;
返回值

size() == 0


size
size_type size() const noexcept;
返回值

std::distance(begin(), end())


max_size
size_type max_size() const noexcept;
返回值

最大可能容器的 `size()`。


修改器

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)构造的元素插入到容器中。

要求

value_type可以从kEmplaceConstructible构建。

返回值

返回类型的布尔分量如果进行了插入则为 true。

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。

指向元素的指针和引用永远不会失效。

只有当Hash::is_transparentPred::is_transparent是有效的成员typedef,并且iteratorconst_iterator都不能从K隐式转换时,此重载才参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型的实例的成本。


带提示的复制插入
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` 是关于应插入元素位置的建议。

要求

value_type可以从kEmplaceConstructible构建。

返回值

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。

可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。

指向元素的指针和引用永远不会失效。

只有当Hash::is_transparentPred::is_transparent是有效的成员typedef,并且iteratorconst_iterator都不能从K隐式转换时,此重载才参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型的实例的成本。


插入迭代器范围
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中,可以使用此方法提取的节点插入到兼容的unordered_multiset中,但这目前还不支持。


按值提取
node_type extract(const key_type& k);
template<class K> node_type extract(K&& k);

移除键值等效于k的元素。

返回值

如果找到,则返回拥有该元素的node_type,否则返回空的node_type

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

在C++17中,可以使用此方法提取的节点插入到兼容的unordered_multiset中,但这目前还不支持。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义,并且iteratorconst_iterator都不能隐式转换为K时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


使用node_handle插入
insert_return_type insert(node_type&& nh);

如果nh为空,则没有效果。

否则,当且仅当容器中没有具有等效键的元素时,插入nh拥有的元素。

要求

nh为空或nh.get_allocator()等于容器的分配器。

返回值

如果nh为空,则返回一个insert_return_type,其中:inserted等于falseposition等于end()node为空。

否则,如果已经存在具有等效键的元素,则返回一个insert_return_type,其中:inserted等于falseposition指向匹配的元素,node包含来自nh的节点。

否则,如果插入成功,则返回一个insert_return_type,其中:inserted等于trueposition指向新插入的元素,node为空。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。

指向元素的指针和引用永远不会失效。

在C++17中,这可以用来插入从兼容的unordered_multiset中提取的节点,但这目前还不支持。


使用提示和node_handle插入
iterator insert(const_iterator hint, node_type&& nh);

如果nh为空,则没有效果。

否则,当且仅当容器中没有具有等效键的元素时,插入nh拥有的元素。

如果容器中已经存在具有等效键的元素,则对nh没有影响(即nh仍然包含该节点)。

`hint` 是关于应插入元素位置的建议。

要求

nh为空或nh.get_allocator()等于容器的分配器。

返回值

如果nh为空,则返回end()

如果容器中已经存在具有等效键的元素,则返回指向该元素的迭代器。

否则,返回指向新插入元素的迭代器。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。

可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。

指向元素的指针和引用永远不会失效。

这可以用来插入从兼容的unordered_multiset中提取的节点。


按位置删除
iterator erase(iterator position);
iterator erase(const_iterator position);

删除position指向的元素。

返回值

删除之前position之后的迭代器。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

在旧版本中,这可能效率低下,因为它必须搜索多个桶才能找到返回迭代器的位置。数据结构已更改,因此这种情况不再发生,并且替代删除方法已被弃用。


按值删除
size_type erase(const key_type& k);
template<class K> size_type erase(K&& k);

删除所有键值等效于k的元素。

返回值

删除的元素数量。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义,并且iteratorconst_iterator都不能隐式转换为K时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


删除范围
iterator erase(const_iterator first, const_iterator last);

删除从firstlast范围内的元素。

返回值

删除元素之后的迭代器 - 即last

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。


快速删除(quick_erase)
void quick_erase(const_iterator position);

删除position指向的元素。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。

注释

实现此方法是因为从erase返回指向下一个元素的迭代器代价很高,但是容器已经重新设计,这种情况不再发生。因此,此方法现已弃用。


返回void的删除 (erase_return_void)
void erase_return_void(const_iterator position);

删除position指向的元素。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。

注释

实现此方法是因为从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::valuetrue,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。

抛出

除非key_equalhasher的复制构造函数或复制赋值运算符抛出异常,否则不会抛出异常。

注释

异常规范与C++11标准略有不同,因为相等谓词和哈希函数使用它们的复制构造函数进行交换。


清空 (clear)
void clear() noexcept;

删除容器中的所有元素。

后置条件

size() == 0

抛出

从不抛出异常。


合并 (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

注释
  • 指向已转移元素的指针和引用保持有效。

  • 使指向已转移元素的迭代器失效。

  • 使属于*this的迭代器失效。

  • 指向source中未转移元素的迭代器保持有效。


观察者

获取分配器 (get_allocator)
allocator_type get_allocator() const;

哈希函数 (hash_function)
hasher hash_function() const;
返回值

容器的哈希函数。


键相等谓词 (key_eq)
key_equal key_eq() const;
返回值

容器的键相等谓词。


查找

查找 (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;
返回值

指向键值等效于k的元素的迭代器,如果不存在这样的元素,则为b.end()

注释

包含CompatibleKeyCompatibleHashCompatiblePredicate的模板重载是非标准扩展,允许你使用不同类型的键的兼容哈希函数和相等谓词,以避免代价高昂的类型转换。一般来说,不鼓励使用它,而应该使用K成员函数模板。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


计数 (count)
size_type        count(const key_type& k) const;
template<class K>
  size_type      count(const K& k) const;
返回值

键值等效于k的元素数量。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


包含 (contains)
bool             contains(const key_type& k) const;
template<class K>
  bool           contains(const K& k) const;
返回值

一个布尔值,指示容器中是否存在键等于key的元素。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


相等范围 (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;
返回值

包含所有键值等效于k的元素的范围。如果容器不包含任何此类元素,则返回std::make_pair(b.end(), b.end())

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


桶接口

桶数量 (bucket_count)
size_type bucket_count() const noexcept;
返回值

桶的数量。


最大桶数量 (max_bucket_count)
size_type max_bucket_count() const noexcept;
返回值

桶数量的上限。


桶大小 (bucket_size)
size_type bucket_size(size_type n) const;
要求

n < bucket_count()

返回值

n 中的元素数量。


桶 (bucket)
size_type bucket(const key_type& k) const;
template<class K> size_type bucket(const K& k) const;
返回值

将包含键为k的元素的桶的索引。

后置条件

返回值小于bucket_count()

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


begin
local_iterator begin(size_type n);
const_local_iterator begin(size_type n) const;
要求

n应在范围[0, bucket_count())内。

返回值

指向索引为n的桶中第一个元素的局部迭代器。


end
local_iterator end(size_type n);
const_local_iterator end(size_type n) const;
要求

n应在范围[0, bucket_count())内。

返回值

指向索引为n的桶中“最后一个元素之后”的局部迭代器。


cbegin
const_local_iterator cbegin(size_type n) const;
要求

n应在范围[0, bucket_count())内。

返回值

指向索引为n的桶中第一个元素的常量局部迭代器。


cend
const_local_iterator cend(size_type n) const;
要求

n应在范围[0, bucket_count())内。

返回值

指向索引为n的桶中“最后一个元素之后”的常量局部迭代器。


哈希策略

负载因子 (load_factor)
float load_factor() const noexcept;
返回值

每个桶的平均元素数量。


最大负载因子 (max_load_factor)
float max_load_factor() const noexcept;
返回值

返回当前最大负载因子。


设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果

使用z作为提示更改容器的最大负载因子。


重新哈希 (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)));

交换xy的内容。

如果声明了Allocator::propagate_on_container_swap并且Allocator::propagate_on_container_swap::valuetrue,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。

效果

x.swap(y)

抛出

除非key_equalhasher的复制构造函数或复制赋值运算符抛出异常,否则不会抛出异常。

注释

异常规范与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的元素。

返回值

被移除元素的数量。

注释

等价于

auto original_size = c.size();
for (auto i = c.begin(), last = c.end(); i != last; ) {
  if (pred(*i)) {
    i = c.erase(i);
  } else {
    ++i;
  }
}
return original_size - c.size();

序列化

可以使用此库提供的API通过Boost.Serialization存档/检索unordered_set。支持常规存档和XML存档。

将unordered_set保存到存档

unordered_set x的所有元素保存到存档(XML存档)ar中。

要求

value_type是可序列化的(XML可序列化的),并且它支持Boost.Serialization save_construct_data/load_construct_data协议(由DefaultConstructible类型自动支持)。


从存档加载unordered_set

删除unordered_set x中所有预先存在的元素,并从存档(XML存档)ar中插入原始unordered_set other保存到ar读取的存储中的元素的已恢复副本。

要求

value_typeMoveInsertablex.key_equal()的功能等同于other.key_equal()

注意

如果存档是使用Boost 1.84之前的Boost版本保存的,则必须全局定义配置宏BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0才能使此操作成功;否则,将抛出异常。


将迭代器/const_iterator保存到存档

iteratorconst_iteratorit的位置信息保存到存档(XML存档)ar中。it可以是end()迭代器。

要求

it指向的unordered_set x之前已保存到ar中,并且在保存x和保存it之间没有对x进行修改操作。


从存档加载迭代器/const_iterator

使iteratorconst_iteratorit指向原始iteratorconst_iterator)在存档(XML存档)ar读取的存储中保存的已恢复位置。

要求

如果xit指向的unordered_set,则在加载x和加载it之间没有对x进行修改操作。

类模板 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>>;
  }
}

描述

模板参数

Key 必须能够从容器中擦除(即 allocator_traits 可以销毁它)。

哈希

一个一元函数对象类型,充当 Key 的哈希函数。它接受一个类型为 Key 的参数,并返回一个类型为 std::size_t 的值。

Pred

一个二元函数对象,它在 Key 类型的值上实现等价关系。一个二元函数对象,它在 Key 类型的值上引入等价关系。它接受两个类型为 Key 的参数,并返回一个布尔值。

分配器

一个分配器,其值类型与容器的值类型相同。支持使用花哨指针的分配器。

元素被组织成桶。具有相同哈希码的键存储在同一个桶中,并且具有等效键的元素彼此相邻存储。

桶的数量可以通过调用插入操作自动增加,或者作为调用重新哈希的结果。

配置宏

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 的最大负载因子来构造一个空容器。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


桶计数构造函数
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` 个桶的空容器。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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());

使用 `hf` 作为哈希函数,`eql` 作为键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器,并将 `[f, l)` 区间内的元素插入其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


复制构造函数
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` 需要是 可默认构造的


分配器构造函数
explicit unordered_multiset(const Allocator& a);

使用分配器 `a` 构造一个空容器。


带分配器的复制构造函数
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` 中的元素插入其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


带分配器的桶计数构造函数
unordered_multiset(size_type n, const allocator_type& a);

使用 `hf` 作为哈希函数,使用默认的哈希函数和键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。

后置条件

size() == 0

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


带哈希函数和分配器的桶计数构造函数
unordered_multiset(size_type n, const hasher& hf, const allocator_type& a);

使用 `hf` 作为哈希函数,使用默认的键相等谓词,`a` 作为分配器,最大负载因子为 `1.0`,构造一个至少包含 `n` 个桶的空容器。

后置条件

size() == 0

要求

`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();
注意

析构函数将应用于每个元素,并且所有内存都将被释放。


赋值

复制赋值
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` 可移动构造。


初始化列表赋值
unordered_multiset& operator=(std::initializer_list<value_type> il);

从初始化列表中的值赋值。所有现有元素要么被新元素覆盖,要么被销毁。

要求

`value_type` 可 复制插入 到容器中,并且可 复制赋值


迭代器

begin
iterator       begin() noexcept;
const_iterator begin() const noexcept;
返回值

指向容器第一个元素的迭代器,如果容器为空,则返回容器的尾后值。


end
iterator       end() noexcept;
const_iterator end() const noexcept;
返回值

指向容器尾后值的迭代器。


cbegin
const_iterator cbegin() const noexcept;
返回值

指向容器第一个元素的 `const_iterator`,如果容器为空,则返回容器的尾后值。


cend
const_iterator cend() const noexcept;
返回值

指向容器尾后值的 `const_iterator`。


大小和容量

empty
[[nodiscard]] bool empty() const noexcept;
返回值

size() == 0


size
size_type size() const noexcept;
返回值

std::distance(begin(), end())


max_size
size_type max_size() const noexcept;
返回值

最大可能容器的 `size()`。


修改器

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`。

注释

使用此方法提取的节点可以插入到兼容的unordered_set中。


按值提取
node_type extract(const key_type& k);
template<class K> node_type extract(K&& k);

移除键值等效于k的元素。

返回值

如果找到,则返回拥有该元素的node_type,否则返回空的node_type

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

使用此方法提取的节点可以插入到兼容的unordered_set中。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义,并且iteratorconst_iterator都不能隐式转换为K时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


使用node_handle插入
iterator insert(node_type&& nh);

如果nh为空,则没有效果。

否则插入nh拥有的元素。

要求

nh为空或nh.get_allocator()等于容器的分配器。

返回值

如果nh为空,则返回end()

否则,返回指向新插入元素的迭代器。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。

指向元素的指针和引用永远不会失效。

这可以用来插入从兼容的unordered_set中提取的节点。


使用提示和node_handle插入
iterator insert(const_iterator hint, node_type&& nh);

如果nh为空,则没有效果。

否则插入nh拥有的元素。

`hint` 是关于应插入元素位置的建议。

要求

nh为空或nh.get_allocator()等于容器的分配器。

返回值

如果nh为空,则返回end()

否则,返回指向新插入元素的迭代器。

抛出

如果由除对hasher的调用以外的操作抛出异常,则该函数无效。

注释

标准对提示的含义相当模糊。但是,使用它的唯一实用方法,以及 Boost.Unordered 支持的唯一方法是,指向具有相同键的现有元素。

可能会使迭代器失效,但仅当插入导致负载因子大于或等于最大负载因子时。

指向元素的指针和引用永远不会失效。

这可以用来插入从兼容的unordered_set中提取的节点。


按位置删除
iterator erase(iterator position);
iterator erase(const_iterator position);

删除position指向的元素。

返回值

删除之前position之后的迭代器。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

在旧版本中,这可能效率低下,因为它必须搜索多个桶才能找到返回迭代器的位置。数据结构已更改,因此这种情况不再发生,并且替代删除方法已被弃用。


按值删除
size_type erase(const key_type& k);
template<class K> size_type erase(K&& x);

删除所有键值等效于k的元素。

返回值

删除的元素数量。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义,并且iteratorconst_iterator都不能隐式转换为K时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


删除范围
iterator erase(const_iterator first, const_iterator last);

删除从firstlast范围内的元素。

返回值

删除元素之后的迭代器 - 即last

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。


快速删除(quick_erase)
void quick_erase(const_iterator position);

删除position指向的元素。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。

注释

实现此方法是因为从erase返回指向下一个元素的迭代器代价很高,但是容器已经重新设计,这种情况不再发生。因此,此方法现已弃用。


返回void的删除 (erase_return_void)
void erase_return_void(const_iterator position);

删除position指向的元素。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

在此实现中,此重载不调用任何函数对象的成员方法,因此不会抛出异常,但在其他实现中可能并非如此。

注释

实现此方法是因为从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::valuetrue,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。

抛出

除非key_equalhasher的复制构造函数或复制赋值运算符抛出异常,否则不会抛出异常。

注释

异常规范与C++11标准略有不同,因为相等谓词和哈希函数使用它们的复制构造函数进行交换。


清空 (clear)
void clear() noexcept;

删除容器中的所有元素。

后置条件

size() == 0

抛出

从不抛出异常。


合并 (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

注释
  • 指向已转移元素的指针和引用保持有效。

  • 使指向已转移元素的迭代器失效。

  • 使属于*this的迭代器失效。

  • 指向source中未转移元素的迭代器保持有效。


观察者

获取分配器 (get_allocator)
allocator_type get_allocator() const noexcept;

哈希函数 (hash_function)
hasher hash_function() const;
返回值

容器的哈希函数。


键相等谓词 (key_eq)
key_equal key_eq() const;
返回值

容器的键相等谓词。


查找

查找 (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;
返回值

指向键值等效于k的元素的迭代器,如果不存在这样的元素,则为b.end()

注释

包含CompatibleKeyCompatibleHashCompatiblePredicate的模板重载是非标准扩展,允许你使用不同类型的键的兼容哈希函数和相等谓词,以避免代价高昂的类型转换。一般来说,不鼓励使用它,而应该使用K成员函数模板。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


计数 (count)
size_type        count(const key_type& k) const;
template<class K>
  size_type      count(const K& k) const;
返回值

键值等效于k的元素数量。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


包含 (contains)
bool             contains(const key_type& k) const;
template<class K>
  bool           contains(const K& k) const;
返回值

一个布尔值,指示容器中是否存在键等于key的元素。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


相等范围 (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;
返回值

包含所有键值等效于k的元素的范围。如果容器不包含任何此类元素,则返回std::make_pair(b.end(), b.end())

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


桶接口

桶数量 (bucket_count)
size_type bucket_count() const noexcept;
返回值

桶的数量。


最大桶数量 (max_bucket_count)
size_type max_bucket_count() const noexcept;
返回值

桶数量的上限。


桶大小 (bucket_size)
size_type bucket_size(size_type n) const;
要求

n < bucket_count()

返回值

n 中的元素数量。


桶 (bucket)
size_type bucket(const key_type& k) const;
template<class K> size_type bucket(const K& k) const;
返回值

将包含键为k的元素的桶的索引。

后置条件

返回值小于bucket_count()

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


begin
local_iterator begin(size_type n);
const_local_iterator begin(size_type n) const;
要求

n应在范围[0, bucket_count())内。

返回值

指向索引为n的桶中第一个元素的局部迭代器。


end
local_iterator end(size_type n);
const_local_iterator end(size_type n) const;
要求

n应在范围[0, bucket_count())内。

返回值

指向索引为n的桶中“最后一个元素之后”的局部迭代器。


cbegin
const_local_iterator cbegin(size_type n) const;
要求

n应在范围[0, bucket_count())内。

返回值

指向索引为n的桶中第一个元素的常量局部迭代器。


cend
const_local_iterator cend(size_type n) const;
要求

n应在范围[0, bucket_count())内。

返回值

指向索引为n的桶中“最后一个元素之后”的常量局部迭代器。


哈希策略

负载因子 (load_factor)
float load_factor() const noexcept;
返回值

每个桶的平均元素数量。


最大负载因子 (max_load_factor)
float max_load_factor() const noexcept;
返回值

返回当前最大负载因子。


设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果

使用z作为提示更改容器的最大负载因子。


重新哈希 (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)));

交换xy的内容。

如果声明了Allocator::propagate_on_container_swap并且Allocator::propagate_on_container_swap::valuetrue,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。

效果

x.swap(y)

抛出

除非key_equalhasher的复制构造函数或复制赋值运算符抛出异常,否则不会抛出异常。

注释

异常规范与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的元素。

返回值

被移除元素的数量。

注释

等价于

auto original_size = c.size();
for (auto i = c.begin(), last = c.end(); i != last; ) {
  if (pred(*i)) {
    i = c.erase(i);
  } else {
    ++i;
  }
}
return original_size - c.size();

序列化

可以使用此库提供的API通过Boost.Serialization存档/检索unordered_multiset。支持常规存档和XML存档。

将unordered_multiset保存到存档

unordered_multiset x的所有元素保存到存档(XML存档)ar中。

要求

value_type是可序列化的(XML可序列化的),并且它支持Boost.Serialization save_construct_data/load_construct_data协议(由DefaultConstructible类型自动支持)。


从存档加载unordered_multiset

删除unordered_multiset x中所有预先存在的元素,并从存档(XML存档)ar中插入原始unordered_multiset other保存到ar读取的存储中的元素的已恢复副本。

要求

value_typeMoveInsertablex.key_equal()的功能等同于other.key_equal()

注意

如果存档是使用Boost 1.84之前的Boost版本保存的,则必须全局定义配置宏BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0才能使此操作成功;否则,将抛出异常。


将迭代器/const_iterator保存到存档

iteratorconst_iteratorit的位置信息保存到存档(XML存档)ar中。it可以是end()迭代器。

要求

it指向的unordered_multiset x之前已保存到ar中,并且在保存x和保存it之间没有对x进行修改操作。


从存档加载迭代器/const_iterator

使iteratorconst_iteratorit指向原始iteratorconst_iterator)在存档(XML存档)ar读取的存储中保存的已恢复位置。

要求

如果xit指向的unordered_multiset,则在加载x和加载it之间没有对x进行修改操作。

哈希特性

概要

// #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_avalanchingvoid(此用法已弃用);

  • 否则为非法的。

用户可以通过将合适的is_avalanching类型定义嵌入到Hash的定义中,或者直接将hash_is_avalanching<Hash>特化为包含嵌入式编译时常量value(设置为true)的类来声明哈希函数Hash具有雪崩效应。

如果hash_is_avalanching<Hash>::valuetrue,则开放寻址和并发容器按原样使用提供的哈希函数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>>>;
  }
}

描述

模板参数

KeyT必须是可移动构造的。std::pair<const Key, T>必须是可就地构造的,可以从任何可转换为它的std::pair对象在容器中就地构造,并且它也必须是可删除的。

T

哈希

一个一元函数对象类型,充当 Key 的哈希函数。它接受一个类型为 Key 的参数,并返回一个类型为 std::size_t 的值。

Pred

一个二元函数对象,它在Key类型的数值上诱导等价关系。它接受两个Key类型的参数并返回一个bool类型的数值。

分配器

一个分配器,其值类型与容器的值类型相同。支持使用花哨指针的分配器。

容器的元素保存在内部的桶数组中。元素插入到由其哈希码确定的桶中,但如果桶已被占用(冲突),则使用原始位置附近的可用桶。

桶数组的大小可以通过调用insert/emplace自动增加,或者通过调用rehash/reserve来增加。容器的负载因子(元素数量除以桶的数量)永远不会大于max_load_factor(),除了在小型尺寸下,实现可能决定允许更高的负载。

如果hash_is_avalanching<Hash>::valuetrue,则哈希函数按原样使用;否则,会添加一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。


配置宏

BOOST_UNORDERED_ENABLE_STATS

全局定义此宏以启用容器的统计数据计算。请注意,此选项会降低许多操作的整体性能。


类型定义

typedef implementation-defined iterator;

一个迭代器,其值类型为 value_type

迭代器类别至少是前向迭代器。

可转换为 const_iterator


typedef implementation-defined const_iterator;

一个常量迭代器,其值类型为 value_type

迭代器类别至少是前向迭代器。

构造函数

默认构造函数
unordered_flat_map();

使用hasher()作为哈希函数、key_equal()作为键相等谓词和allocator_type()作为分配器来构造一个空容器。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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个桶的空容器。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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());

使用hf作为哈希函数、eql作为键相等谓词和a作为分配器来构造一个至少包含n个桶的空容器,并将[f, l)中的元素插入到其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


复制构造函数
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` 需要是 可默认构造的


分配器构造函数
explicit unordered_flat_map(Allocator const& a);

使用分配器 `a` 构造一个空容器。


带分配器的复制构造函数
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()

复杂度

常数时间。

并发性

阻塞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());

使用hf作为哈希函数、eql作为键相等谓词和a来构造一个至少包含n个桶的空容器,并将il中的元素插入到其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


带分配器的桶计数构造函数
unordered_flat_map(size_type n, allocator_type const& a);

使用hf作为哈希函数、默认的哈希函数和键相等谓词以及a作为分配器来构造一个至少包含n个桶的空容器。

后置条件

size() == 0

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


带哈希函数和分配器的桶计数构造函数
unordered_flat_map(size_type n, hasher const& hf, allocator_type const& a);

使用hf作为哈希函数、默认的键相等谓词和a作为分配器来构造一个至少包含n个桶的空容器。

后置条件

size() == 0

要求

`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();
注意

析构函数将应用于每个元素,并且所有内存都将被释放。


赋值

复制赋值
unordered_flat_map& operator=(unordered_flat_map const& other);

赋值运算符。销毁先前存在的元素,从other复制赋值哈希函数和谓词,如果Alloc::propagate_on_container_copy_assignment存在且Alloc::propagate_on_container_copy_assignment::valuetrue,则从other复制赋值分配器,最后插入other元素的副本。

要求

value_type可复制插入的。


移动赋值
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::valuetrue,则从other移动赋值分配器。如果此时分配器等于other.get_allocator(),则other的内部桶数组直接转移到新容器;否则,插入other元素的移动构造的副本。如果启用了统计数据,则当且仅当最终分配器等于other.get_allocator()时,才从other转移内部统计信息,并且始终调用other.reset_stats()


初始化列表赋值
unordered_flat_map& operator=(std::initializer_list<value_type> il);

从初始化列表中赋值。所有先前存在的元素都将被销毁。

要求

value_type可复制插入的。

迭代器

begin
iterator begin() noexcept;
const_iterator begin() const noexcept;
返回值

指向容器第一个元素的迭代器,如果容器为空,则返回容器的尾后值。

复杂度

O(bucket_count())


end
iterator end() noexcept;
const_iterator end() const noexcept;
返回值

指向容器尾后值的迭代器。


cbegin
const_iterator cbegin() const noexcept;
返回值

指向容器第一个元素的 `const_iterator`,如果容器为空,则返回容器的尾后值。

复杂度

O(bucket_count())


cend
const_iterator cend() const noexcept;
返回值

指向容器尾后值的 `const_iterator`。


大小和容量

empty
[[nodiscard]] bool empty() const noexcept;
返回值

size() == 0


size
size_type size() const noexcept;
返回值

std::distance(begin(), end())


max_size
size_type max_size() const noexcept;
返回值

最大可能容器的 `size()`。


修改器

emplace
template<class... Args> std::pair<iterator, bool> emplace(Args&&... args);

当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。

要求

value_type 可由 args 构造。

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。

如果 args…​ 的形式为 k,v,它会延迟构造整个对象,直到确定应该插入元素为止,只使用 k 参数进行检查。


emplace_hint
    template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);

当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。

position 是关于应插入元素位置的建议。此实现忽略它。

要求

value_type 可由 args 构造。

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。

如果 args…​ 的形式为 k,v,它会延迟构造整个对象,直到确定应该插入元素为止,只使用 k 参数进行检查。


复制插入
std::pair<iterator, bool> insert(const value_type& obj);
std::pair<iterator, bool> insert(const init_type& obj);

当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。

要求

`value_type` 可 复制插入

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。

x 可同时转换为 const value_type&const init_type& 时,调用 insert(x) 不会产生歧义,并选择 init_type 重载。


移动插入
std::pair<iterator, bool> insert(value_type&& obj);
std::pair<iterator, bool> insert(init_type&& obj);

当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。

要求

`value_type` 可 移动插入

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。

x 可同时转换为 value_type&&init_type&& 时,调用 insert(x) 不会产生歧义,并选择 init_type 重载。


带提示的复制插入
iterator insert(const_iterator hint, const value_type& obj);
iterator insert(const_iterator hint, const init_type& obj);

当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。

hint 是关于应插入元素位置的建议。此实现忽略它。

要求

`value_type` 可 复制插入

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。

x 可同时转换为 const value_type&const init_type& 时,调用 insert(hint, x) 不会产生歧义,并选择 init_type 重载。


带提示的移动插入
iterator insert(const_iterator hint, value_type&& obj);
iterator insert(const_iterator hint, init_type&& obj);

当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。

hint 是关于应插入元素位置的建议。此实现忽略它。

要求

`value_type` 可 移动插入

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。

x 可同时转换为 value_type&&init_type&& 时,调用 insert(hint, x) 不会产生歧义,并选择 init_type 重载。


插入迭代器范围
template<class InputIterator> void insert(InputIterator first, InputIterator last);

将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。

要求

value_type 可以从 *first 就地构造 到容器中。

抛出

插入单个元素时,如果除对 `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` 的元素,则此函数不执行任何操作。

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

此函数类似于 emplace,不同之处在于,如果存在具有等效键的元素,则不会构造 value_type;否则,构造形式为

// first two overloads
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<Key>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

// third overload
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<K>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

不同于 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;否则,构造形式为

// first two overloads
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<Key>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

// third overload
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<K>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

不同于 emplace_hint,它只是将所有参数转发到 value_type 的构造函数。

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。

`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)))
返回值

返回类型中的 bool 分量在插入发生时为 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` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。

`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指向的元素。

返回值

一个不透明对象,可以隐式转换为擦除之前紧跟在 position 后的 iteratorconst_iterator

抛出

无。

注释

返回的不透明对象只能被丢弃或立即转换为 iteratorconst_iterator


按键删除
size_type erase(const key_type& k);
template<class K> size_type erase(K&& k);

删除所有键值等效于k的元素。

返回值

删除的元素数量。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义,并且iteratorconst_iterator都不能隐式转换为K时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


删除范围
iterator erase(const_iterator first, const_iterator last);

删除从firstlast范围内的元素。

返回值

删除元素之后的迭代器 - 即last

抛出

此实现中没有任何操作(既不调用 hasher 对象也不调用 key_equal 对象)。


交换 (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::valuetrue,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。

抛出

除非 key_equalhasher 在交换时抛出异常。


清空 (clear)
void clear() noexcept;

删除容器中的所有元素。

后置条件

size() == 0, max_load() >= max_load_factor() * bucket_count()


合并 (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 中删除这些元素。


观察者

获取分配器 (get_allocator)
allocator_type get_allocator() const noexcept;
返回值

容器的分配器。


哈希函数 (hash_function)
hasher hash_function() const;
返回值

容器的哈希函数。


键相等谓词 (key_eq)
key_equal key_eq() const;
返回值

容器的键相等谓词。


查找

查找 (find)
iterator         find(const key_type& k);
const_iterator   find(const key_type& k) const;
template<class K>
  iterator       find(const K& k);
返回值

指向具有与 k 等效键的元素的迭代器,如果不存在这样的元素,则为 end()

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


计数 (count)
size_type        count(const key_type& k) const;
template<class K>
  size_type      count(const K& k) const;
返回值

键值等效于k的元素数量。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


包含 (contains)
bool             contains(const key_type& k) const;
template<class K>
  bool           contains(const K& k) const;
返回值

一个布尔值,指示容器中是否存在键等于key的元素。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


相等范围 (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;
返回值

包含所有键值等效于k的元素的范围。如果容器不包含任何此类元素,则返回std::make_pair(b.end(), b.end())

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


下标运算符 (operator[])
mapped_type& operator[](const key_type& k);
mapped_type& operator[](key_type&& k);
template<class K> mapped_type& operator[](K&& k);
效果

如果容器还不包含具有与 k 等效键的元素,则插入值 std::pair<key_type const, mapped_type>(k, mapped_type())

返回值

x.second的引用,其中x是容器中已存在的元素,或新插入的键值等效于k的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


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;
返回值

x.second的引用,其中x是键值等效于k的(唯一)元素。

抛出

如果不存在这样的元素,则抛出类型为std::out_of_range的异常对象。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


桶接口

桶数量 (bucket_count)
size_type bucket_count() const noexcept;
返回值

桶数组的大小。


哈希策略

负载因子 (load_factor)
float load_factor() const noexcept;
返回值

static_cast<float>(size())/static_cast<float>(bucket_count()),如果 bucket_count() == 0,则为 0


最大负载因子 (max_load_factor)
float max_load_factor() const noexcept;
返回值

返回容器的最大负载因子。


设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果

不执行任何操作,因为不允许用户更改此参数。为了与 boost::unordered_map 保持兼容性而保留。


最大负载
size_type max_load() const noexcept;
返回值

容器在不重新哈希的情况下可以容纳的最大元素数量,假设不再删除元素。

注意

构造、重新哈希或清除后,容器的最大负载至少为 max_load_factor() * bucket_count()。在高负载条件下,此数字可能会在删除时减少。


重新哈希 (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类似,此函数可用于增加或减少容器中桶的数量。

使迭代器、指针和引用失效,并更改元素的顺序。

抛出

除非容器的哈希函数或比较函数抛出异常,否则该函数无效。


统计

获取统计信息
stats get_stats() const;
返回值

到目前为止容器执行的插入和查找操作的统计描述。

注释

仅当 统计计算启用 时才可用。


重置统计信息
void reset_stats() noexcept;
效果

将容器维护的内部统计信息设置为零。

注释

仅当 统计计算启用 时才可用。


推导指南

如果出现以下任何情况,则推导指南不会参与重载解析:

  • 它具有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)));

交换xy的内容。

如果声明了Allocator::propagate_on_container_swap并且Allocator::propagate_on_container_swap::valuetrue,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。

效果

x.swap(y)

抛出

除非 key_equalhasher 在交换时抛出异常。


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的元素。

返回值

被移除元素的数量。

注释

等价于

auto original_size = c.size();
for (auto i = c.begin(), last = c.end(); i != last; ) {
  if (pred(*i)) {
    i = c.erase(i);
  } else {
    ++i;
  }
}
return original_size - c.size();

序列化

可以使用此库提供的 API 通过 Boost.Serialization 存档/检索 unordered_flat_map。支持常规存档和 XML 存档。

将 unordered_flat_map 保存到存档

unordered_flat_map x 的所有元素保存到存档(XML 存档)ar 中。

要求

std::remove_const<key_type>::typestd::remove_const<mapped_type>::type是可序列化的(XML可序列化的),并且它们支持Boost.Serialization save_construct_data/load_construct_data协议(由DefaultConstructible类型自动支持)。


从存档加载 unordered_flat_map

删除 unordered_flat_map x 中所有预先存在的元素,并从存档(XML 存档)ar 中插入原始 unordered_flat_map other 元素的已还原副本(这些元素已保存到 ar 读取的存储区中)。

要求

x.key_equal() 的功能等效于 other.key_equal()


将迭代器/const_iterator保存到存档

iteratorconst_iteratorit的位置信息保存到存档(XML存档)ar中。it可以是end()迭代器。

要求

it 指向的 unordered_flat_map x 之前已保存到 ar 中,并且在保存 x 和保存 it 之间没有对 x 执行任何修改操作。


从存档加载迭代器/const_iterator

使iteratorconst_iteratorit指向原始iteratorconst_iterator)在存档(XML存档)ar读取的存储中保存的已恢复位置。

要求

如果 xit 指向的 unordered_flat_map,则在加载 x 和加载 it 之间没有对 x 执行任何修改操作。

类模板 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>>;
  }
}

描述

模板参数

Key 必须可以移动插入 到容器中,并可以从容器中删除

哈希

一个一元函数对象类型,充当 Key 的哈希函数。它接受一个类型为 Key 的参数,并返回一个类型为 std::size_t 的值。

Pred

一个二元函数对象,它在Key类型的数值上诱导等价关系。它接受两个Key类型的参数并返回一个bool类型的数值。

分配器

一个分配器,其值类型与容器的值类型相同。支持使用花哨指针的分配器。

容器的元素保存在内部的桶数组中。元素插入到由其哈希码确定的桶中,但如果桶已被占用(冲突),则使用原始位置附近的可用桶。

桶数组的大小可以通过调用insert/emplace自动增加,或者通过调用rehash/reserve来增加。容器的负载因子(元素数量除以桶的数量)永远不会大于max_load_factor(),除了在小型尺寸下,实现可能决定允许更高的负载。

如果hash_is_avalanching<Hash>::valuetrue,则哈希函数按原样使用;否则,会添加一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。


配置宏

BOOST_UNORDERED_ENABLE_STATS

全局定义此宏以启用容器的统计数据计算。请注意,此选项会降低许多操作的整体性能。


类型定义

typedef implementation-defined iterator;

一个常量迭代器,其值类型为 value_type

迭代器类别至少是前向迭代器。

可转换为 const_iterator


typedef implementation-defined const_iterator;

一个常量迭代器,其值类型为 value_type

迭代器类别至少是前向迭代器。

构造函数

默认构造函数
unordered_flat_set();

使用hasher()作为哈希函数、key_equal()作为键相等谓词和allocator_type()作为分配器来构造一个空容器。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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个桶的空容器。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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());

使用hf作为哈希函数、eql作为键相等谓词和a作为分配器来构造一个至少包含n个桶的空容器,并将[f, l)中的元素插入到其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


复制构造函数
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` 需要是 可默认构造的


分配器构造函数
explicit unordered_flat_set(Allocator const& a);

使用分配器 `a` 构造一个空容器。


带分配器的复制构造函数
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()

复杂度

常数时间。

并发性

阻塞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());

使用hf作为哈希函数、eql作为键相等谓词和a来构造一个至少包含n个桶的空容器,并将il中的元素插入到其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


带分配器的桶计数构造函数
unordered_flat_set(size_type n, allocator_type const& a);

使用hf作为哈希函数、默认的哈希函数和键相等谓词以及a作为分配器来构造一个至少包含n个桶的空容器。

后置条件

size() == 0

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


带哈希函数和分配器的桶计数构造函数
unordered_flat_set(size_type n, hasher const& hf, allocator_type const& a);

使用hf作为哈希函数、默认的键相等谓词和a作为分配器来构造一个至少包含n个桶的空容器。

后置条件

size() == 0

要求

`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();
注意

析构函数将应用于每个元素,并且所有内存都将被释放。


赋值

复制赋值
unordered_flat_set& operator=(unordered_flat_set const& other);

赋值运算符。销毁先前存在的元素,从other复制赋值哈希函数和谓词,如果Alloc::propagate_on_container_copy_assignment存在且Alloc::propagate_on_container_copy_assignment::valuetrue,则从other复制赋值分配器,最后插入other元素的副本。

要求

value_type可复制插入的。


移动赋值
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::valuetrue,则从 other 移动赋值分配器。如果此时分配器等于 other.get_allocator(),则 other 的内部桶数组将直接转移到新容器;否则,将插入 other 元素的移动构造副本。如果启用了统计信息 启用,则当且仅当最终分配器等于 other.get_allocator() 时,才会从 other 传输内部统计信息,并且始终调用 other.reset_stats()


初始化列表赋值
unordered_flat_set& operator=(std::initializer_list<value_type> il);

从初始化列表中赋值。所有先前存在的元素都将被销毁。

要求

value_type可复制插入的。

迭代器

begin
iterator begin() noexcept;
const_iterator begin() const noexcept;
返回值

指向容器第一个元素的迭代器,如果容器为空,则返回容器的尾后值。

复杂度

O(bucket_count())


end
iterator end() noexcept;
const_iterator end() const noexcept;
返回值

指向容器尾后值的迭代器。


cbegin
const_iterator cbegin() const noexcept;
返回值

指向容器第一个元素的 `const_iterator`,如果容器为空,则返回容器的尾后值。

复杂度

O(bucket_count())


cend
const_iterator cend() const noexcept;
返回值

指向容器尾后值的 `const_iterator`。


大小和容量

empty
[[nodiscard]] bool empty() const noexcept;
返回值

size() == 0


size
size_type size() const noexcept;
返回值

std::distance(begin(), end())


max_size
size_type max_size() const noexcept;
返回值

最大可能容器的 `size()`。


修改器

emplace
template<class... Args> std::pair<iterator, bool> emplace(Args&&... args);

当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。

要求

value_type 可由 args 构造。

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。


emplace_hint
    template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);

当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。

position 是关于应插入元素位置的建议。此实现忽略它。

要求

value_type 可由 args 构造。

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。


复制插入
std::pair<iterator, bool> insert(const value_type& obj);

当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。

要求

`value_type` 可 复制插入

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。


移动插入
std::pair<iterator, bool> insert(value_type&& obj);

当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。

要求

`value_type` 可 移动插入

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。


透明插入
template<class K> std::pair<iterator, bool> insert(K&& k);

当且仅当容器中没有具有等效键的元素时,才将从std::forward<K>(k)构造的元素插入到容器中。

要求

value_type可以从kEmplaceConstructible构建。

返回值

返回类型的布尔分量如果进行了插入则为 true。

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。

只有当Hash::is_transparentPred::is_transparent是有效的成员typedef,并且iteratorconst_iterator都不能从K隐式转换时,此重载才参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型的实例的成本。


带提示的复制插入
iterator insert(const_iterator hint, const value_type& obj);

当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。

hint 是关于应插入元素位置的建议。此实现忽略它。

要求

`value_type` 可 复制插入

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。


带提示的移动插入
iterator insert(const_iterator hint, value_type&& obj);

当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。

hint 是关于应插入元素位置的建议。此实现忽略它。

要求

`value_type` 可 移动插入

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。


带提示的透明插入
template<class K> std::pair<iterator, bool> insert(const_iterator hint, K&& k);

当且仅当容器中没有具有等效键的元素时,才将从std::forward<K>(k)构造的元素插入到容器中。

hint 是关于应插入元素位置的建议。此实现忽略它。

要求

value_type可以从kEmplaceConstructible构建。

返回值

返回类型的布尔分量如果进行了插入则为 true。

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器、指针和引用失效,但仅当插入导致负载超过最大负载时。

只有当Hash::is_transparentPred::is_transparent是有效的成员typedef,并且iteratorconst_iterator都不能从K隐式转换时,此重载才参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型的实例的成本。


插入迭代器范围
template<class InputIterator> void insert(InputIterator first, InputIterator last);

将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。

要求

value_type 可以从 *first 就地构造 到容器中。

抛出

插入单个元素时,如果除对 `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指向的元素。

返回值

一个不透明对象,可以隐式转换为擦除之前紧跟在 position 后的 iteratorconst_iterator

抛出

无。

注释

返回的不透明对象只能被丢弃或立即转换为 iteratorconst_iterator


按键删除
size_type erase(const key_type& k);
template<class K> size_type erase(K&& k);

删除所有键值等效于k的元素。

返回值

删除的元素数量。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义,并且iteratorconst_iterator都不能隐式转换为K时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


删除范围
iterator erase(const_iterator first, const_iterator last);

删除从firstlast范围内的元素。

返回值

删除元素之后的迭代器 - 即last

抛出

此实现中没有任何操作(既不调用 hasher 对象也不调用 key_equal 对象)。


交换 (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::valuetrue,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。

抛出

除非 key_equalhasher 在交换时抛出异常。


清空 (clear)
void clear() noexcept;

删除容器中的所有元素。

后置条件

size() == 0, max_load() >= max_load_factor() * bucket_count()


合并 (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 中删除这些元素。


观察者

获取分配器 (get_allocator)
allocator_type get_allocator() const noexcept;
返回值

容器的分配器。


哈希函数 (hash_function)
hasher hash_function() const;
返回值

容器的哈希函数。


键相等谓词 (key_eq)
key_equal key_eq() const;
返回值

容器的键相等谓词。


查找

查找 (find)
iterator         find(const key_type& k);
const_iterator   find(const key_type& k) const;
template<class K>
  iterator       find(const K& k);
返回值

指向具有与 k 等效键的元素的迭代器,如果不存在这样的元素,则为 end()

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


计数 (count)
size_type        count(const key_type& k) const;
template<class K>
  size_type      count(const K& k) const;
返回值

键值等效于k的元素数量。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


包含 (contains)
bool             contains(const key_type& k) const;
template<class K>
  bool           contains(const K& k) const;
返回值

一个布尔值,指示容器中是否存在键等于key的元素。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


相等范围 (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;
返回值

包含所有键值等效于k的元素的范围。如果容器不包含任何此类元素,则返回std::make_pair(b.end(), b.end())

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


桶接口

桶数量 (bucket_count)
size_type bucket_count() const noexcept;
返回值

桶数组的大小。


哈希策略

负载因子 (load_factor)
float load_factor() const noexcept;
返回值

static_cast<float>(size())/static_cast<float>(bucket_count()),如果 bucket_count() == 0,则为 0


最大负载因子 (max_load_factor)
float max_load_factor() const noexcept;
返回值

返回容器的最大负载因子。


设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果

不执行任何操作,因为不允许用户更改此参数。为了与 boost::unordered_set 保持兼容性而保留。


最大负载
size_type max_load() const noexcept;
返回值

容器在不重新哈希的情况下可以容纳的最大元素数量,假设不再删除元素。

注意

构造、重新哈希或清除后,容器的最大负载至少为 max_load_factor() * bucket_count()。在高负载条件下,此数字可能会在删除时减少。


重新哈希 (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类似,此函数可用于增加或减少容器中桶的数量。

使迭代器、指针和引用失效,并更改元素的顺序。

抛出

除非容器的哈希函数或比较函数抛出异常,否则该函数无效。


统计

获取统计信息
stats get_stats() const;
返回值

到目前为止容器执行的插入和查找操作的统计描述。

注释

仅当 统计计算启用 时才可用。


重置统计信息
void reset_stats() noexcept;
效果

将容器维护的内部统计信息设置为零。

注释

仅当 统计计算启用 时才可用。


推导指南

如果出现以下任何情况,则推导指南不会参与重载解析:

  • 它具有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)));

交换xy的内容。

如果声明了Allocator::propagate_on_container_swap并且Allocator::propagate_on_container_swap::valuetrue,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。

效果

x.swap(y)

抛出

除非 key_equalhasher 在交换时抛出异常。


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的元素。

返回值

被移除元素的数量。

注释

等价于

auto original_size = c.size();
for (auto i = c.begin(), last = c.end(); i != last; ) {
  if (pred(*i)) {
    i = c.erase(i);
  } else {
    ++i;
  }
}
return original_size - c.size();

序列化

可以使用此库提供的 API 通过 Boost.Serialization 存档/检索 unordered_flat_set。支持常规存档和 XML 存档。

将 unordered_flat_set 保存到存档

unordered_flat_set x 的所有元素保存到存档(XML 存档)ar 中。

要求

value_type是可序列化的(XML可序列化的),并且它支持Boost.Serialization save_construct_data/load_construct_data协议(由DefaultConstructible类型自动支持)。


从存档加载 unordered_flat_set

删除 unordered_flat_set x 中所有预先存在的元素,并从存档(XML 存档)ar 中插入原始 unordered_flat_set other 元素的已还原副本(这些元素已保存到 ar 读取的存储区中)。

要求

x.key_equal() 的功能等效于 other.key_equal()


将迭代器/const_iterator保存到存档

iteratorconst_iteratorit的位置信息保存到存档(XML存档)ar中。it可以是end()迭代器。

要求

it 指向的 unordered_flat_set x 之前已保存到 ar 中,并且在保存 x 和保存 it 之间没有对 x 执行任何修改操作。


从存档加载迭代器/const_iterator

使iteratorconst_iteratorit指向原始iteratorconst_iterator)在存档(XML存档)ar读取的存储中保存的已恢复位置。

要求

如果 xit 指向的 unordered_flat_set,则在加载 x 和加载 it 之间没有对 x 执行任何修改操作。

类模板 unordered_node_map

boost::unordered_node_map — 一个基于节点的开放寻址无序关联容器,用于将唯一键与另一个值关联。

boost::unordered_node_map 使用类似于 boost::unordered_flat_map 的开放寻址布局,但由于它是基于节点的,因此它提供了指针稳定性和节点处理功能。它的性能介于 boost::unordered_mapboost::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>>>;
  }
}

描述

模板参数

std::pair<const Key, T> 必须能够从任何可转换为它的 std::pair 对象在容器中就地构造,并且还必须能够从容器中删除

T

哈希

一个一元函数对象类型,充当 Key 的哈希函数。它接受一个类型为 Key 的参数,并返回一个类型为 std::size_t 的值。

Pred

一个二元函数对象,它在Key类型的数值上诱导等价关系。它接受两个Key类型的参数并返回一个bool类型的数值。

分配器

一个分配器,其值类型与容器的值类型相同。支持使用花哨指针的分配器。

容器的元素节点存储在一个内部的桶数组中。节点插入到由其元素的哈希码确定的桶中,但是如果桶已被占用(发生冲突),则使用原始位置附近的可用桶。

桶数组的大小可以通过调用insert/emplace自动增加,或者通过调用rehash/reserve来增加。容器的负载因子(元素数量除以桶的数量)永远不会大于max_load_factor(),除了在小型尺寸下,实现可能决定允许更高的负载。

如果hash_is_avalanching<Hash>::valuetrue,则哈希函数按原样使用;否则,会添加一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。


配置宏

BOOST_UNORDERED_ENABLE_STATS

全局定义此宏以启用容器的统计数据计算。请注意,此选项会降低许多操作的整体性能。


类型定义

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 = iteratorNodeType = node_type


构造函数

默认构造函数
unordered_node_map();

使用hasher()作为哈希函数、key_equal()作为键相等谓词和allocator_type()作为分配器来构造一个空容器。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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个桶的空容器。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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());

使用hf作为哈希函数、eql作为键相等谓词和a作为分配器来构造一个至少包含n个桶的空容器,并将[f, l)中的元素插入到其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


复制构造函数
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` 需要是 可默认构造的


分配器构造函数
explicit unordered_node_map(Allocator const& a);

使用分配器 `a` 构造一个空容器。


带分配器的复制构造函数
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()

复杂度

常数时间。

并发性

阻塞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());

使用hf作为哈希函数、eql作为键相等谓词和a来构造一个至少包含n个桶的空容器,并将il中的元素插入到其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


带分配器的桶计数构造函数
unordered_node_map(size_type n, allocator_type const& a);

使用hf作为哈希函数、默认的哈希函数和键相等谓词以及a作为分配器来构造一个至少包含n个桶的空容器。

后置条件

size() == 0

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


带哈希函数和分配器的桶计数构造函数
unordered_node_map(size_type n, hasher const& hf, allocator_type const& a);

使用hf作为哈希函数、默认的键相等谓词和a作为分配器来构造一个至少包含n个桶的空容器。

后置条件

size() == 0

要求

`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();
注意

析构函数将应用于每个元素,并且所有内存都将被释放。


赋值

复制赋值
unordered_node_map& operator=(unordered_node_map const& other);

赋值运算符。销毁先前存在的元素,从other复制赋值哈希函数和谓词,如果Alloc::propagate_on_container_copy_assignment存在且Alloc::propagate_on_container_copy_assignment::valuetrue,则从other复制赋值分配器,最后插入other元素的副本。

要求

value_type可复制插入的。


移动赋值
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::valuetrue,则从 other 移动赋值分配器。如果此时分配器等于 other.get_allocator(),则 other 的内部桶数组直接转移到新的容器;否则,插入 other 元素的移动构造的副本。如果启用了统计信息 enabled,则当且仅当最终分配器等于 other.get_allocator() 时,才将内部统计信息从 other 传输,并且始终调用 other.reset_stats()


初始化列表赋值
unordered_node_map& operator=(std::initializer_list<value_type> il);

从初始化列表中赋值。所有先前存在的元素都将被销毁。

要求

value_type可复制插入的。

迭代器

begin
iterator begin() noexcept;
const_iterator begin() const noexcept;
返回值

指向容器第一个元素的迭代器,如果容器为空,则返回容器的尾后值。

复杂度

O(bucket_count())


end
iterator end() noexcept;
const_iterator end() const noexcept;
返回值

指向容器尾后值的迭代器。


cbegin
const_iterator cbegin() const noexcept;
返回值

指向容器第一个元素的 `const_iterator`,如果容器为空,则返回容器的尾后值。

复杂度

O(bucket_count())


cend
const_iterator cend() const noexcept;
返回值

指向容器尾后值的 `const_iterator`。


大小和容量

empty
[[nodiscard]] bool empty() const noexcept;
返回值

size() == 0


size
size_type size() const noexcept;
返回值

std::distance(begin(), end())


max_size
size_type max_size() const noexcept;
返回值

最大可能容器的 `size()`。


修改器

emplace
template<class... Args> std::pair<iterator, bool> emplace(Args&&... args);

当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。

要求

value_type 可由 args 构造。

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。

如果 args…​ 的形式为 k,v,它会延迟构造整个对象,直到确定应该插入元素为止,只使用 k 参数进行检查。当 key_type 是可移动构造的,或者 k 参数是 key_type 时,会发生这种优化。


emplace_hint
    template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);

当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。

position 是关于应插入元素位置的建议。此实现忽略它。

要求

value_type 可由 args 构造。

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。

如果 args…​ 的形式为 k,v,它会延迟构造整个对象,直到确定应该插入元素为止,只使用 k 参数进行检查。当 key_type 是可移动构造的,或者 k 参数是 key_type 时,会发生这种优化。


复制插入
std::pair<iterator, bool> insert(const value_type& obj);
std::pair<iterator, bool> insert(const init_type& obj);

当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。

要求

`value_type` 可 复制插入

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。

x 可同时转换为 const value_type&const init_type& 时,调用 insert(x) 不会产生歧义,并选择 init_type 重载。


移动插入
std::pair<iterator, bool> insert(value_type&& obj);
std::pair<iterator, bool> insert(init_type&& obj);

当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。

要求

`value_type` 可 移动插入

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。

x 可同时转换为 value_type&&init_type&& 时,调用 insert(x) 不会产生歧义,并选择 init_type 重载。


带提示的复制插入
iterator insert(const_iterator hint, const value_type& obj);
iterator insert(const_iterator hint, const init_type& obj);

当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。

hint 是关于应插入元素位置的建议。此实现忽略它。

要求

`value_type` 可 复制插入

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。

x 可同时转换为 const value_type&const init_type& 时,调用 insert(hint, x) 不会产生歧义,并选择 init_type 重载。


带提示的移动插入
iterator insert(const_iterator hint, value_type&& obj);
iterator insert(const_iterator hint, init_type&& obj);

当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。

hint 是关于应插入元素位置的建议。此实现忽略它。

要求

`value_type` 可 移动插入

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。

x 可同时转换为 value_type&&init_type&& 时,调用 insert(hint, x) 不会产生歧义,并选择 init_type 重载。


插入迭代器范围
template<class InputIterator> void insert(InputIterator first, InputIterator last);

将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。

要求

value_type 可以从 *first 就地构造 到容器中。

抛出

插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。


插入初始化列表
void insert(std::initializer_list<value_type>);

将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。

要求

`value_type` 可 复制插入 到容器中。

抛出

插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。


插入节点
insert_return_type insert(node_type&& nh);

如果 nh 不为空,当且仅当容器中没有键与 nh.key() 等效的元素时,才将关联的元素插入到容器中。函数返回时,nh 为空。

返回值

positioninsertednode 构造的 insert_return_type 对象

  • 如果 nh 为空,则 insertedfalsepositionend()node 为空。

  • 否则,如果进行了插入,则 inserted 为 true,position 指向插入的元素,node 为空。

  • 如果插入失败,则 inserted 为 false,node 具有 nh 的先前值,position 指向键与 nh.key() 等效的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

如果 nh 不为空且 nh 和容器的分配器不相等,则行为未定义。


使用提示插入节点
iterator insert(const_iterator hint, node_type&& nh);

如果 nh 不为空,当且仅当容器中没有键与 nh.key() 等效的元素时,才将关联的元素插入到容器中。如果进行了插入,则 nh 变成空,否则不变。

hint 是关于应插入元素位置的建议。此实现忽略它。

返回值

如果 nh 为空,则返回的迭代器为 end()。如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

如果 nh 不为空且 nh 和容器的分配器不相等,则行为未定义。


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` 的元素,则此函数不执行任何操作。

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

此函数类似于 emplace,不同之处在于,如果存在具有等效键的元素,则不构造 value_type;否则,构造形式为

// first two overloads
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<Key>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

// third overload
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<K>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

不同于 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;否则,构造形式为

// first two overloads
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<Key>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

// third overload
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<K>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

不同于 emplace_hint,它只是将所有参数转发到 value_type 的构造函数。

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。

`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)))
返回值

返回类型中的 bool 分量在插入发生时为 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` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。

`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指向的元素。

返回值

一个不透明对象,可以隐式转换为擦除之前紧跟在 position 后的 iteratorconst_iterator

抛出

无。

注释

返回的不透明对象只能被丢弃或立即转换为 iteratorconst_iterator


按键删除
size_type erase(const key_type& k);
template<class K> size_type erase(K&& k);

删除所有键值等效于k的元素。

返回值

删除的元素数量。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义,并且iteratorconst_iterator都不能隐式转换为K时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


删除范围
iterator erase(const_iterator first, const_iterator last);

删除从firstlast范围内的元素。

返回值

删除元素之后的迭代器 - 即last

抛出

此实现中没有任何操作(既不调用 hasher 对象也不调用 key_equal 对象)。


交换 (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::valuetrue,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。

抛出

除非 key_equalhasher 在交换时抛出异常。


按位置提取
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_type 对象,如果未提取任何元素,则为空。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


清空 (clear)
void clear() noexcept;

删除容器中的所有元素。

后置条件

size() == 0, max_load() >= max_load_factor() * bucket_count()


合并 (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 中不存在的元素节点。

要求

this->get_allocator() == source.get_allocator().

注释

使指向已传输元素的迭代器失效。如果 *this 的结果大小大于其原始最大负载,则使与 *this 关联的所有迭代器失效。


观察者

获取分配器 (get_allocator)
allocator_type get_allocator() const noexcept;
返回值

容器的分配器。


哈希函数 (hash_function)
hasher hash_function() const;
返回值

容器的哈希函数。


键相等谓词 (key_eq)
key_equal key_eq() const;
返回值

容器的键相等谓词。


查找

查找 (find)
iterator         find(const key_type& k);
const_iterator   find(const key_type& k) const;
template<class K>
  iterator       find(const K& k);
返回值

指向具有与 k 等效键的元素的迭代器,如果不存在这样的元素,则为 end()

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


计数 (count)
size_type        count(const key_type& k) const;
template<class K>
  size_type      count(const K& k) const;
返回值

键值等效于k的元素数量。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


包含 (contains)
bool             contains(const key_type& k) const;
template<class K>
  bool           contains(const K& k) const;
返回值

一个布尔值,指示容器中是否存在键等于key的元素。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


相等范围 (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;
返回值

包含所有键值等效于k的元素的范围。如果容器不包含任何此类元素,则返回std::make_pair(b.end(), b.end())

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


下标运算符 (operator[])
mapped_type& operator[](const key_type& k);
mapped_type& operator[](key_type&& k);
template<class K> mapped_type& operator[](K&& k);
效果

如果容器还不包含具有与 k 等效键的元素,则插入值 std::pair<key_type const, mapped_type>(k, mapped_type())

返回值

x.second的引用,其中x是容器中已存在的元素,或新插入的键值等效于k的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


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;
返回值

x.second的引用,其中x是键值等效于k的(唯一)元素。

抛出

如果不存在这样的元素,则抛出类型为std::out_of_range的异常对象。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


桶接口

桶数量 (bucket_count)
size_type bucket_count() const noexcept;
返回值

桶数组的大小。


哈希策略

负载因子 (load_factor)
float load_factor() const noexcept;
返回值

static_cast<float>(size())/static_cast<float>(bucket_count()),如果 bucket_count() == 0,则为 0


最大负载因子 (max_load_factor)
float max_load_factor() const noexcept;
返回值

返回容器的最大负载因子。


设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果

不执行任何操作,因为不允许用户更改此参数。为了与 boost::unordered_map 保持兼容性而保留。


最大负载
size_type max_load() const noexcept;
返回值

容器在不重新哈希的情况下可以容纳的最大元素数量,假设不再删除元素。

注意

构造、重新哈希或清除后,容器的最大负载至少为 max_load_factor() * bucket_count()。在高负载条件下,此数字可能会在删除时减少。


重新哈希 (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类似,此函数可用于增加或减少容器中桶的数量。

使迭代器失效并更改元素的顺序。

抛出

除非容器的哈希函数或比较函数抛出异常,否则该函数无效。


统计

获取统计信息
stats get_stats() const;
返回值

到目前为止容器执行的插入和查找操作的统计描述。

注释

仅当启用了 统计计算 enabled 时才可用。


重置统计信息
void reset_stats() noexcept;
效果

将容器维护的内部统计信息设置为零。

注释

仅当启用了 统计计算 enabled 时才可用。


推导指南

如果出现以下任何情况,则推导指南不会参与重载解析:

  • 它具有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)));

交换xy的内容。

如果声明了Allocator::propagate_on_container_swap并且Allocator::propagate_on_container_swap::valuetrue,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。

效果

x.swap(y)

抛出

除非 key_equalhasher 在交换时抛出异常。


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的元素。

返回值

被移除元素的数量。

注释

等价于

auto original_size = c.size();
for (auto i = c.begin(), last = c.end(); i != last; ) {
  if (pred(*i)) {
    i = c.erase(i);
  } else {
    ++i;
  }
}
return original_size - c.size();

序列化

可以使用此库提供的 API,通过 Boost.Serialization 存档/检索 unordered_node_map。支持常规存档和 XML 存档。

将 unordered_node_map 保存到存档

unordered_node_map x 的所有元素保存到存档(XML 存档)ar 中。

要求

std::remove_const<key_type>::typestd::remove_const<mapped_type>::type是可序列化的(XML可序列化的),并且它们支持Boost.Serialization save_construct_data/load_construct_data协议(由DefaultConstructible类型自动支持)。


从存档加载 unordered_node_map

删除 unordered_node_map x 中所有预先存在的元素,并从存档(XML 存档)ar 中插入原始 unordered_node_map other 的元素的已恢复副本(这些元素已保存到 ar 读取的存储区中)。

要求

key_typemapped_type 分别可以从 std::remove_const<key_type>::type&&std::remove_const<mapped_type>::type&& 构造。x.key_equal()other.key_equal() 功能等效。


将迭代器/const_iterator保存到存档

iteratorconst_iteratorit的位置信息保存到存档(XML存档)ar中。it可以是end()迭代器。

要求

it 指向的 unordered_node_map x 已先前保存到 ar 中,并且在保存 x 和保存 it 之间没有对 x 进行修改操作。


从存档加载迭代器/const_iterator

使iteratorconst_iteratorit指向原始iteratorconst_iterator)在存档(XML存档)ar读取的存储中保存的已恢复位置。

要求

如果 xit 指向的 unordered_node_map,则在加载 x 和加载 it 之间没有对 x 进行修改操作。

类模板 unordered_node_set

boost::unordered_node_set — 一个基于节点的、开放寻址的无序关联容器,用于存储唯一值。

boost::unordered_node_set 使用类似于 boost::unordered_flat_set 的开放寻址布局,但由于它是基于节点的,因此它提供了指针稳定性和节点处理功能。其性能介于 boost::unordered_setboost::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>>;
  }
}

描述

模板参数

Key 必须能够从容器中删除

哈希

一个一元函数对象类型,充当 Key 的哈希函数。它接受一个类型为 Key 的参数,并返回一个类型为 std::size_t 的值。

Pred

一个二元函数对象,它在Key类型的数值上诱导等价关系。它接受两个Key类型的参数并返回一个bool类型的数值。

分配器

一个分配器,其值类型与容器的值类型相同。支持使用花哨指针的分配器。

容器的元素节点存储在一个内部的桶数组中。节点插入到由其元素的哈希码确定的桶中,但是如果桶已被占用(发生冲突),则使用原始位置附近的可用桶。

桶数组的大小可以通过调用insert/emplace自动增加,或者通过调用rehash/reserve来增加。容器的负载因子(元素数量除以桶的数量)永远不会大于max_load_factor(),除了在小型尺寸下,实现可能决定允许更高的负载。

如果hash_is_avalanching<Hash>::valuetrue,则哈希函数按原样使用;否则,会添加一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。


配置宏

BOOST_UNORDERED_ENABLE_STATS

全局定义此宏以启用容器的统计数据计算。请注意,此选项会降低许多操作的整体性能。


类型定义

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 = iteratorNodeType = node_type


构造函数

默认构造函数
unordered_node_set();

使用hasher()作为哈希函数、key_equal()作为键相等谓词和allocator_type()作为分配器来构造一个空容器。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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个桶的空容器。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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());

使用hf作为哈希函数、eql作为键相等谓词和a作为分配器来构造一个至少包含n个桶的空容器,并将[f, l)中的元素插入到其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


复制构造函数
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` 需要是 可默认构造的


分配器构造函数
explicit unordered_node_set(Allocator const& a);

使用分配器 `a` 构造一个空容器。


带分配器的复制构造函数
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()

复杂度

常数时间。

并发性

阻塞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());

使用hf作为哈希函数、eql作为键相等谓词和a来构造一个至少包含n个桶的空容器,并将il中的元素插入到其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


带分配器的桶计数构造函数
unordered_node_set(size_type n, allocator_type const& a);

使用hf作为哈希函数、默认的哈希函数和键相等谓词以及a作为分配器来构造一个至少包含n个桶的空容器。

后置条件

size() == 0

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


带哈希函数和分配器的桶计数构造函数
unordered_node_set(size_type n, hasher const& hf, allocator_type const& a);

使用hf作为哈希函数、默认的键相等谓词和a作为分配器来构造一个至少包含n个桶的空容器。

后置条件

size() == 0

要求

`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();
注意

析构函数将应用于每个元素,并且所有内存都将被释放。


赋值

复制赋值
unordered_node_set& operator=(unordered_node_set const& other);

赋值运算符。销毁先前存在的元素,从other复制赋值哈希函数和谓词,如果Alloc::propagate_on_container_copy_assignment存在且Alloc::propagate_on_container_copy_assignment::valuetrue,则从other复制赋值分配器,最后插入other元素的副本。

要求

value_type可复制插入的。


移动赋值
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::valuetrue,则从 other 移动赋值分配器。如果此时分配器等于 other.get_allocator(),则 other 的内部桶数组直接转移到新的容器;否则,插入 other 元素的移动构造的副本。如果启用了统计信息 enabled,则当且仅当最终分配器等于 other.get_allocator() 时,才将内部统计信息从 other 传输,并且始终调用 other.reset_stats()


初始化列表赋值
unordered_node_set& operator=(std::initializer_list<value_type> il);

从初始化列表中赋值。所有先前存在的元素都将被销毁。

要求

value_type可复制插入的。

迭代器

begin
iterator begin() noexcept;
const_iterator begin() const noexcept;
返回值

指向容器第一个元素的迭代器,如果容器为空,则返回容器的尾后值。

复杂度

O(bucket_count())


end
iterator end() noexcept;
const_iterator end() const noexcept;
返回值

指向容器尾后值的迭代器。


cbegin
const_iterator cbegin() const noexcept;
返回值

指向容器第一个元素的 `const_iterator`,如果容器为空,则返回容器的尾后值。

复杂度

O(bucket_count())


cend
const_iterator cend() const noexcept;
返回值

指向容器尾后值的 `const_iterator`。


大小和容量

empty
[[nodiscard]] bool empty() const noexcept;
返回值

size() == 0


size
size_type size() const noexcept;
返回值

std::distance(begin(), end())


max_size
size_type max_size() const noexcept;
返回值

最大可能容器的 `size()`。


修改器

emplace
template<class... Args> std::pair<iterator, bool> emplace(Args&&... args);

当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。

要求

value_type 可由 args 构造。

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。


emplace_hint
    template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);

当且仅当容器中没有具有等效键的元素时,将使用参数 `args` 构造的对象插入容器中。

position 是关于应插入元素位置的建议。此实现忽略它。

要求

value_type 可由 args 构造。

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。


复制插入
std::pair<iterator, bool> insert(const value_type& obj);

当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。

要求

`value_type` 可 复制插入

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。


移动插入
std::pair<iterator, bool> insert(value_type&& obj);

当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。

要求

`value_type` 可 移动插入

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。


透明插入
template<class K> std::pair<iterator, bool> insert(K&& k);

当且仅当容器中没有具有等效键的元素时,才将从std::forward<K>(k)构造的元素插入到容器中。

要求

value_type可以从kEmplaceConstructible构建。

返回值

返回类型的布尔分量如果进行了插入则为 true。

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。

只有当Hash::is_transparentPred::is_transparent是有效的成员typedef,并且iteratorconst_iterator都不能从K隐式转换时,此重载才参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型的实例的成本。


带提示的复制插入
iterator insert(const_iterator hint, const value_type& obj);

当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。

hint 是关于应插入元素位置的建议。此实现忽略它。

要求

`value_type` 可 复制插入

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。


带提示的移动插入
iterator insert(const_iterator hint, value_type&& obj);

当且仅当容器中没有具有等效键的元素时,将 `obj` 插入容器中。

hint 是关于应插入元素位置的建议。此实现忽略它。

要求

`value_type` 可 移动插入

返回值

返回类型中的 bool 分量在插入发生时为 true

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。


带提示的透明插入
template<class K> std::pair<iterator, bool> insert(const_iterator hint, K&& k);

当且仅当容器中没有具有等效键的元素时,才将从std::forward<K>(k)构造的元素插入到容器中。

hint 是关于应插入元素位置的建议。此实现忽略它。

要求

value_type可以从kEmplaceConstructible构建。

返回值

返回类型的布尔分量如果进行了插入则为 true。

如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。

只有当Hash::is_transparentPred::is_transparent是有效的成员typedef,并且iteratorconst_iterator都不能从K隐式转换时,此重载才参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型的实例的成本。


插入迭代器范围
template<class InputIterator> void insert(InputIterator first, InputIterator last);

将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。

要求

value_type 可以从 *first 就地构造 到容器中。

抛出

插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。


插入初始化列表
void insert(std::initializer_list<value_type>);

将一系列元素插入容器中。当且仅当容器中没有具有等效键的元素时,才插入元素。

要求

`value_type` 可 复制插入 到容器中。

抛出

插入单个元素时,如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

可能会使迭代器失效,但仅当插入导致负载大于最大负载时。


插入节点
insert_return_type insert(node_type&& nh);

如果 nh 不为空,当且仅当容器中没有键与 nh.value() 等效的元素时,才将关联的元素插入到容器中。函数返回时,nh 为空。

返回值

positioninsertednode 构造的 insert_return_type 对象

  • 如果 nh 为空,则 insertedfalsepositionend()node 为空。

  • 否则,如果进行了插入,则 inserted 为 true,position 指向插入的元素,node 为空。

  • 如果插入失败,则 inserted 为 false,node 具有 nh 的先前值,position 指向键与 nh.value() 等效的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

如果 nh 不为空且 nh 和容器的分配器不相等,则行为未定义。


使用提示插入节点
iterator insert(const_iterator hint, node_type&& nh);

如果 nh 不为空,当且仅当容器中没有键与 nh.value() 等效的元素时,才将关联的元素插入到容器中。如果进行了插入,则 nh 变成空,否则不变。

hint 是关于应插入元素位置的建议。此实现忽略它。

返回值

如果 nh 为空,则返回的迭代器为 end()。如果进行了插入,则迭代器指向新插入的元素;否则,它指向具有等效键的元素。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

注释

如果 nh 不为空且 nh 和容器的分配器不相等,则行为未定义。


按位置删除
convertible-to-iterator erase(iterator position);
convertible-to-iterator erase(const_iterator position);

删除position指向的元素。

返回值

一个不透明对象,可以隐式转换为擦除之前紧跟在 position 后的 iteratorconst_iterator

抛出

无。

注释

返回的不透明对象只能被丢弃或立即转换为 iteratorconst_iterator


按键删除
size_type erase(const key_type& k);
template<class K> size_type erase(K&& k);

删除所有键值等效于k的元素。

返回值

删除的元素数量。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义,并且iteratorconst_iterator都不能隐式转换为K时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


删除范围
iterator erase(const_iterator first, const_iterator last);

删除从firstlast范围内的元素。

返回值

删除元素之后的迭代器 - 即last

抛出

此实现中没有任何操作(既不调用 hasher 对象也不调用 key_equal 对象)。


交换 (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::valuetrue,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。

抛出

除非 key_equalhasher 在交换时抛出异常。


按位置提取
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_type 对象,如果未提取任何元素,则为空。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


清空 (clear)
void clear() noexcept;

删除容器中的所有元素。

后置条件

size() == 0, max_load() >= max_load_factor() * bucket_count()


合并 (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 中不存在的元素节点。

要求

this->get_allocator() == source.get_allocator().

注释

使指向已传输元素的迭代器失效。如果 *this 的结果大小大于其原始最大负载,则使与 *this 关联的所有迭代器失效。


观察者

获取分配器 (get_allocator)
allocator_type get_allocator() const noexcept;
返回值

容器的分配器。


哈希函数 (hash_function)
hasher hash_function() const;
返回值

容器的哈希函数。


键相等谓词 (key_eq)
key_equal key_eq() const;
返回值

容器的键相等谓词。


查找

查找 (find)
iterator         find(const key_type& k);
const_iterator   find(const key_type& k) const;
template<class K>
  iterator       find(const K& k);
返回值

指向具有与 k 等效键的元素的迭代器,如果不存在这样的元素,则为 end()

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


计数 (count)
size_type        count(const key_type& k) const;
template<class K>
  size_type      count(const K& k) const;
返回值

键值等效于k的元素数量。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


包含 (contains)
bool             contains(const key_type& k) const;
template<class K>
  bool           contains(const K& k) const;
返回值

一个布尔值,指示容器中是否存在键等于key的元素。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


相等范围 (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;
返回值

包含所有键值等效于k的元素的范围。如果容器不包含任何此类元素,则返回std::make_pair(b.end(), b.end())

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


桶接口

桶数量 (bucket_count)
size_type bucket_count() const noexcept;
返回值

桶数组的大小。


哈希策略

负载因子 (load_factor)
float load_factor() const noexcept;
返回值

static_cast<float>(size())/static_cast<float>(bucket_count()),如果 bucket_count() == 0,则为 0


最大负载因子 (max_load_factor)
float max_load_factor() const noexcept;
返回值

返回容器的最大负载因子。


设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果

不执行任何操作,因为不允许用户更改此参数。为了与 boost::unordered_set 保持兼容性而保留。


最大负载
size_type max_load() const noexcept;
返回值

容器在不重新哈希的情况下可以容纳的最大元素数量,假设不再删除元素。

注意

构造、重新哈希或清除后,容器的最大负载至少为 max_load_factor() * bucket_count()。在高负载条件下,此数字可能会在删除时减少。


重新哈希 (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类似,此函数可用于增加或减少容器中桶的数量。

使迭代器失效并更改元素的顺序。

抛出

除非容器的哈希函数或比较函数抛出异常,否则该函数无效。


统计

获取统计信息
stats get_stats() const;
返回值

到目前为止容器执行的插入和查找操作的统计描述。

注释

仅当启用了 统计计算 enabled 时才可用。


重置统计信息
void reset_stats() noexcept;
效果

将容器维护的内部统计信息设置为零。

注释

仅当启用了 统计计算 enabled 时才可用。


推导指南

如果出现以下任何情况,则推导指南不会参与重载解析:

  • 它具有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)));

交换xy的内容。

如果声明了Allocator::propagate_on_container_swap并且Allocator::propagate_on_container_swap::valuetrue,则交换容器的分配器。否则,使用不等的分配器进行交换会导致未定义的行为。

效果

x.swap(y)

抛出

除非 key_equalhasher 在交换时抛出异常。


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的元素。

返回值

被移除元素的数量。

注释

等价于

auto original_size = c.size();
for (auto i = c.begin(), last = c.end(); i != last; ) {
  if (pred(*i)) {
    i = c.erase(i);
  } else {
    ++i;
  }
}
return original_size - c.size();

序列化

可以使用此库提供的 API,通过 Boost.Serialization 存档/检索 unordered_node_set。支持常规存档和 XML 存档。

将 unordered_node_set 保存到存档

unordered_node_set x 的所有元素保存到存档(XML 存档)ar 中。

要求

value_type是可序列化的(XML可序列化的),并且它支持Boost.Serialization save_construct_data/load_construct_data协议(由DefaultConstructible类型自动支持)。


从存档加载 unordered_node_set

删除 unordered_node_set x 中所有预先存在的元素,并从存档(XML 存档)ar 中插入原始 unordered_node_set other 的元素的已恢复副本(这些元素已保存到 ar 读取的存储区中)。

要求

value_typeMoveInsertablex.key_equal()的功能等同于other.key_equal()


将迭代器/const_iterator保存到存档

iteratorconst_iteratorit的位置信息保存到存档(XML存档)ar中。it可以是end()迭代器。

要求

it 指向的 unordered_node_set x 已先前保存到 ar 中,并且在保存 x 和保存 it 之间没有对 x 进行修改操作。


从存档加载迭代器/const_iterator

使iteratorconst_iteratorit指向原始iteratorconst_iterator)在存档(XML存档)ar读取的存储中保存的已恢复位置。

要求

如果 xit 指向的 unordered_node_set,则在加载 x 和加载 it 之间没有对 x 进行修改操作。

类模板 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

哈希

一个一元函数对象类型,充当 Key 的哈希函数。它接受一个类型为 Key 的参数,并返回一个类型为 std::size_t 的值。

Pred

一个二元函数对象,它在Key类型的数值上诱导等价关系。它接受两个Key类型的参数并返回一个bool类型的数值。

分配器

一个值类型与表的值类型相同的分配器。支持使用 花哨指针 的分配器。

表的元素保存在内部的桶数组中。元素插入到由其哈希码确定的桶中,但如果该桶已被占用(发生冲突),则使用原始位置附近的一个可用桶。

桶数组的大小可以通过调用 `insert`/`emplace` 自动增加,或者通过调用 `rehash`/`reserve` 来增加。表的负载因子(元素数量除以桶的数量)永远不会大于 `max_load_factor()`,除非在较小的尺寸下,实现可能决定允许更高的负载。

如果hash_is_avalanching<Hash>::valuetrue,则哈希函数按原样使用;否则,会添加一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。


并发需求和保证

对同一 `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` 发出信号。当运行时速度成为关注点时,可以通过全局定义此宏来禁用此功能。


BOOST_UNORDERED_ENABLE_STATS

全局定义此宏以启用表的 统计计算。请注意,此选项会降低许多操作的整体性能。


常量

static constexpr size_type bulk_visit_size;

批量访问 操作中内部使用的块大小。

构造函数

默认构造函数
concurrent_flat_map();

使用 `hasher()` 作为哈希函数、`key_equal()` 作为键相等谓词和 `allocator_type()` 作为分配器构造一个空表。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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` 个桶的空表。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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());

使用 `hf` 作为哈希函数、`eql` 作为键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表,并将元素从 `[f, l)` 插入到其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


复制构造函数
concurrent_flat_map(concurrent_flat_map const& other);

复制构造函数。复制包含的元素、哈希函数、谓词和分配器。

如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。

要求

`value_type` 可复制构造。

并发性

阻塞other


移动构造函数
concurrent_flat_map(concurrent_flat_map&& other);

移动构造函数。`other` 的内部桶数组直接转移到新表。哈希函数、谓词和分配器是从 `other` 移动构造的。如果启用了统计 功能,则将内部统计信息从 `other` 传输并调用 `other.reset_stats()`。

并发性

阻塞other


带分配器的迭代器范围构造函数
template<class InputIterator>
  concurrent_flat_map(InputIterator f, InputIterator l, const allocator_type& a);

使用 `a` 作为分配器,使用默认哈希函数和键相等谓词构造一个空表,并将元素从 `[f, l)` 插入到其中。

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


分配器构造函数
explicit concurrent_flat_map(Allocator const& a);

使用分配器 `a` 构造一个空表。


带分配器的复制构造函数
concurrent_flat_map(concurrent_flat_map const& other, Allocator const& a);

构造一个表,复制 `other` 的包含元素、哈希函数和谓词,但使用分配器 `a`。

并发性

阻塞other


带分配器的移动构造函数
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()`。

并发性

阻塞other


从 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(bucket_count())


初始化列表构造函数
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` 插入到其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


带分配器的桶计数构造函数
concurrent_flat_map(size_type n, allocator_type const& a);

使用 `hf` 作为哈希函数、默认哈希函数和键相等谓词以及 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。

后置条件

size() == 0

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


带哈希函数和分配器的桶计数构造函数
concurrent_flat_map(size_type n, hasher const& hf, allocator_type const& a);

使用 `hf` 作为哈希函数、默认键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。

后置条件

size() == 0

要求

`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();
注意

析构函数将应用于每个元素,并且所有内存都将被释放。


赋值

复制赋值
concurrent_flat_map& operator=(concurrent_flat_map const& other);

赋值运算符。销毁先前存在的元素,从other复制赋值哈希函数和谓词,如果Alloc::propagate_on_container_copy_assignment存在且Alloc::propagate_on_container_copy_assignment::valuetrue,则从other复制赋值分配器,最后插入other元素的副本。

要求

value_type可复制插入的。

并发性

阻塞于 `*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`。


初始化列表赋值
concurrent_flat_map& operator=(std::initializer_list<value_type> il);

从初始化列表中赋值。所有先前存在的元素都将被销毁。

要求

value_type可复制插入的。

并发性

阻塞于 `*this`。


访问

[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才能获得性能提升:超过此大小,性能预计不会进一步提高。

要求

FwdIterator是一个LegacyForwardIterator(C++11到C++17),或者满足std::forward_iterator(C++20及更高版本)。对于K = std::iterator_traits<FwdIterator>::value_typeK要么是key_type,要么Hash::is_transparentPred::is_transparent是有效的成员类型定义。在后一种情况下,库假设Hash可以同时使用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。

返回值

已访问元素的数量。


[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是常量时,这些引用才是常量。根据指定的执行策略的语义并行执行。

抛出

根据所用执行策略的异常处理机制,如果在f中抛出异常,则可能会调用std::terminate

注释

仅在支持C++17并行算法的编译器中可用。

只有当std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>true时,这些重载才参与重载解析。

不允许无序执行策略。


[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是常量时,这些元素引用才是常量。

返回值

如果f曾经返回false,则为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或访问所有元素。只有当*this是常量时,这些元素引用才是常量。根据指定的执行策略的语义并行执行。

返回值

如果f曾经返回false,则为false

抛出

根据所用执行策略的异常处理机制,如果在f中抛出异常,则可能会调用std::terminate

注释

仅在支持C++17并行算法的编译器中可用。

只有当std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>true时,这些重载才参与重载解析。

不允许无序执行策略。

并行化意味着执行并不一定在f返回false时立即结束,因此可能会使用返回值也为false的后续元素调用f


大小和容量

empty
[[nodiscard]] bool empty() const noexcept;
返回值

size() == 0


size
size_type size() const noexcept;
返回值

表中元素的数量。

注释

在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实大小。


max_size
size_type max_size() const noexcept;
返回值

最大可能表的size()


修改器

emplace
template<class... Args> bool emplace(Args&&... args);

仅当表中不存在具有等效键的元素时,才使用参数args构造的对象插入到表中。

要求

value_type 可由 args 构造。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。

如果 args…​ 的形式为 k,v,它会延迟构造整个对象,直到确定应该插入元素为止,只使用 k 参数进行检查。


复制插入
bool insert(const value_type& obj);
bool insert(const init_type& obj);

仅当表中不存在具有等效键的元素时,才将obj插入到表中。

要求

`value_type` 可 复制插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。

x 可同时转换为 const value_type&const init_type& 时,调用 insert(x) 不会产生歧义,并选择 init_type 重载。


移动插入
bool insert(value_type&& obj);
bool insert(init_type&& obj);

仅当表中不存在具有等效键的元素时,才将obj插入到表中。

要求

`value_type` 可 移动插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。

x 可同时转换为 value_type&&init_type&& 时,调用 insert(x) 不会产生歧义,并选择 init_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,则该引用为常量。

要求

value_type 可由 args 构造。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。

该接口仅用于说明,因为C++不允许在可变参数包之后声明参数f


复制 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` 可 复制插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。

insert_or_[c]visit(obj, f)形式的调用中,只有当std::remove_cv<std::remove_reference<decltype(obj)>::type>::typevalue_type时,接受const 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` 可 移动插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。

insert_or_[c]visit(obj, f)形式的调用中,只有当std::remove_reference<decltype(obj)>::typevalue_type时,接受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,则该引用为常量。

要求

value_type 可由 args 构造。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。

该接口仅用于说明,因为C++不允许在可变参数包之后声明参数f1f2


复制 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` 可 复制插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。

insert_and_[c]visit(obj, f1, f2)形式的调用中,只有当std::remove_cv<std::remove_reference<decltype(obj)>::type>::typevalue_type时,接受const 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` 可 移动插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。

insert_and_[c]visit(obj, f1, f2)形式的调用中,只有当std::remove_reference<decltype(obj)>::typevalue_type时,接受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的现有元素,则将使用kargs构造的元素插入到表中。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

此函数类似于emplace,不同之处在于,如果存在具有等效键的元素,则不会构造value_type;否则,构造形式为

// first two overloads
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<Key>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

// third overload
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<K>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

emplace不同,它只是将所有参数转发到value_type的构造函数。

如果发出重新哈希,则使指向元素的指针和引用无效。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K, class... Args>重载才参与重载解析。库假设Hash可以同时使用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。


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的现有元素,则将使用kargs构造的元素插入到表中。否则,使用对等效元素的引用调用f;只有当使用*_cvisit重载时,该引用才是常量。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果存在具有等效键的元素,则不会构造value_type;否则,构造形式为

// first four overloads
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<Key>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

// last two overloads
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<K>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

如果发出重新哈希,则使指向元素的指针和引用无效。

该接口仅用于说明,因为C++不允许在可变参数包之后声明参数f

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K, class... Args, class F>重载才参与重载解析。库假设Hash可以同时使用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。


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的现有元素,则将使用kargs构造的元素插入到表中,然后使用对新创建元素的非常量引用调用f1。否则,使用对等效元素的引用调用f2;只有当使用*_cvisit重载时,该引用才是常量。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果存在具有等效键的元素,则不会构造value_type;否则,构造形式为

// first four overloads
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<Key>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

// last two overloads
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<K>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

如果发出重新哈希,则使指向元素的指针和引用无效。

该接口仅用于说明,因为C++不允许在可变参数包之后声明参数f1f2

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K, class... Args, class F1, class F2>重载才参与重载解析。库假设Hash可以同时使用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。


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)))
返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。

`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)。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


按键删除
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)。

抛出

只有当hasherkey_equalf抛出异常时,才会抛出异常。

注释

只有当std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>false时,template<class K, class F>重载才参与重载解析。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K, class F>重载才参与重载解析。库假设Hash可以同时使用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。


erase_if
template<class F> size_type erase_if(F f);

连续地使用对表中每个元素的引用调用f,并删除f返回true的那些元素。

返回值

删除的元素数量。

抛出

只有当f抛出异常时,才会抛出异常。


并行 erase_if
template<class ExecutionPolicy, class  F> void erase_if(ExecutionPolicy&& policy, F f);

使用对表中每个元素的引用调用f,并删除f返回true的那些元素。根据指定的执行策略的语义并行执行。

抛出

根据所用执行策略的异常处理机制,如果在f中抛出异常,则可能会调用std::terminate

注释

仅在支持C++17并行算法的编译器中可用。

只有当std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>true时,此重载才参与重载解析。

不允许无序执行策略。


交换 (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::valuetrue,则交换表的分配器。否则,使用不相等的分配器进行交换会导致未定义的行为。

抛出

除非 key_equalhasher 在交换时抛出异常。

并发性

阻塞于 `*this` 和 `other`。


清空 (clear)
void clear() noexcept;

删除表中的所有元素。

后置条件

size() == 0, max_load() >= max_load_factor() * bucket_count()

并发性

阻塞于 `*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 中删除这些元素。

返回值

插入的元素数量。

并发性

阻塞*thissource


观察者

获取分配器 (get_allocator)
allocator_type get_allocator() const noexcept;
返回值

表的分配器。


哈希函数 (hash_function)
hasher hash_function() const;
返回值

表的哈希函数。


键相等谓词 (key_eq)
key_equal key_eq() const;
返回值

表的键相等谓词。


映射操作

计数 (count)
size_type        count(const key_type& k) const;
template<class K>
  size_type      count(const K& k) const;
返回值

键与k等效的元素数量(0或1)。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。

在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。


包含 (contains)
bool             contains(const key_type& k) const;
template<class K>
  bool           contains(const K& k) const;
返回值

一个布尔值,指示表中是否存在键等于k的元素。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。

在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。


桶接口

桶数量 (bucket_count)
size_type bucket_count() const noexcept;
返回值

桶数组的大小。


哈希策略

负载因子 (load_factor)
float load_factor() const noexcept;
返回值

static_cast<float>(size())/static_cast<float>(bucket_count()),如果 bucket_count() == 0,则为 0


最大负载因子 (max_load_factor)
float max_load_factor() const noexcept;
返回值

返回表的最大负载因子。


设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果

不执行任何操作,因为不允许用户更改此参数。为了与 boost::unordered_map 保持兼容性而保留。


最大负载
size_type max_load() const noexcept;
返回值

在不重新哈希的情况下,表可以容纳的最大元素数量,假设不会再删除任何元素。

注意

构造、重新哈希或清除后,表的最大负载至少为max_load_factor() * bucket_count()。在高负载条件下,此数字可能会在删除时减少。

在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。


重新哈希 (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`。


统计

获取统计信息
stats get_stats() const;
返回值

到目前为止表执行的插入和查找操作的统计描述。

注释

仅当统计计算启用时才可用。


重置统计信息
void reset_stats() noexcept;
效果

将表保留的内部统计信息设置为零。

注释

仅当统计计算启用时才可用。


推导指南

如果出现以下任何情况,则推导指南不会参与重载解析:

  • 它具有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

并发性

阻塞xy

注释

如果两个表没有等效的相等谓词,则行为未定义。


不相等运算符 (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

并发性

阻塞xy

注释

如果两个表没有等效的相等谓词,则行为未定义。


交换

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

要求

std::remove_const<key_type>::typestd::remove_const<mapped_type>::type是可序列化的(XML可序列化的),并且它们支持Boost.Serialization save_construct_data/load_construct_data协议(由DefaultConstructible类型自动支持)。

并发性

阻塞x


从存档加载concurrent_flat_map

删除concurrent_flat_map x中所有预先存在的元素,并从存档(XML存档)ar插入原始concurrent_flat_map other(已保存到ar读取的存储中)元素的已恢复副本。

要求

x.key_equal() 的功能等效于 other.key_equal()

并发性

阻塞x

类模板 concurrent_flat_set

boost::concurrent_flat_set — 一个哈希表,它存储唯一值并允许并发元素插入、删除、查找和访问,而无需外部同步机制。

尽管它充当容器,但boost::concurrent_flat_set并不符合标准C++ Container概念。特别是,没有提供迭代器和相关操作(beginend等)。元素访问是通过用户提供的_访问函数_完成的,这些函数被传递到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>>;
  }
}

描述

模板参数

Key 必须可以移动插入 到容器中,并可以从容器中删除

哈希

一个一元函数对象类型,充当 Key 的哈希函数。它接受一个类型为 Key 的参数,并返回一个类型为 std::size_t 的值。

Pred

一个二元函数对象,它在Key类型的数值上诱导等价关系。它接受两个Key类型的参数并返回一个bool类型的数值。

分配器

其值类型与表的值类型相同的分配器。std::allocator_traits<Allocator>::pointerstd::allocator_traits<Allocator>::const_pointer必须分别可转换为/从value_type*const value_type*

表的元素保存在内部的桶数组中。元素插入到由其哈希码确定的桶中,但如果该桶已被占用(发生冲突),则使用原始位置附近的一个可用桶。

桶数组的大小可以通过调用 `insert`/`emplace` 自动增加,或者通过调用 `rehash`/`reserve` 来增加。表的负载因子(元素数量除以桶的数量)永远不会大于 `max_load_factor()`,除非在较小的尺寸下,实现可能决定允许更高的负载。

如果hash_is_avalanching<Hash>::valuetrue,则哈希函数按原样使用;否则,会添加一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。


并发需求和保证

对同一 `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,其中xboost::concurrent_flat_set的实例,则先前对x的阻塞操作与op同步。因此,在多线程场景中,对同一concurrent_flat_set的阻塞操作按顺序执行。

如果一个操作仅在发出内部重新哈希时才阻塞于 `x`,则称该操作为阻塞于 `x` 的重新哈希

当由boost::concurrent_flat_set内部执行时,用户提供的访问函数对传递的元素执行的以下操作不会引入数据竞争

  • 读取元素。

  • 非可变修改元素。

  • 可变修改元素。

    • 在接受两个访问函数的容器函数中,始终针对第一个函数。

    • 在名称不包含 `cvisit` 的非 const 容器函数中,针对最后一个(或唯一一个)访问函数。

任何插入或修改元素eboost::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` 发出信号。当运行时速度成为关注点时,可以通过全局定义此宏来禁用此功能。


BOOST_UNORDERED_ENABLE_STATS

全局定义此宏以启用表的 统计计算。请注意,此选项会降低许多操作的整体性能。


常量

static constexpr size_type bulk_visit_size;

批量访问操作中内部使用的块大小。

构造函数

默认构造函数
concurrent_flat_set();

使用 `hasher()` 作为哈希函数、`key_equal()` 作为键相等谓词和 `allocator_type()` 作为分配器构造一个空表。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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` 个桶的空表。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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());

使用 `hf` 作为哈希函数、`eql` 作为键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表,并将元素从 `[f, l)` 插入到其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


复制构造函数
concurrent_flat_set(concurrent_flat_set const& other);

复制构造函数。复制包含的元素、哈希函数、谓词和分配器。

如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。

要求

`value_type` 可复制构造。

并发性

阻塞other


移动构造函数
concurrent_flat_set(concurrent_flat_set&& other);

移动构造函数。other的内部桶数组直接转移到新表。哈希函数、谓词和分配器是从other移动构造的。如果启用了统计信息,则将内部统计信息从other转移并调用other.reset_stats()

并发性

阻塞other


带分配器的迭代器范围构造函数
template<class InputIterator>
  concurrent_flat_set(InputIterator f, InputIterator l, const allocator_type& a);

使用 `a` 作为分配器,使用默认哈希函数和键相等谓词构造一个空表,并将元素从 `[f, l)` 插入到其中。

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


分配器构造函数
explicit concurrent_flat_set(Allocator const& a);

使用分配器 `a` 构造一个空表。


带分配器的复制构造函数
concurrent_flat_set(concurrent_flat_set const& other, Allocator const& a);

构造一个表,复制 `other` 的包含元素、哈希函数和谓词,但使用分配器 `a`。

并发性

阻塞other


带分配器的移动构造函数
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()

并发性

阻塞other


从unordered_flat_set的移动构造函数
concurrent_flat_set(unordered_flat_set<Key, Hash, Pred, Allocator>&& other);

unordered_flat_set移动构造。other的内部桶数组直接转移到新的容器。哈希函数、谓词和分配器是从other移动构造的。如果启用了统计信息,则将内部统计信息从other转移并调用other.reset_stats()

复杂度

O(bucket_count())


初始化列表构造函数
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` 插入到其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


带分配器的桶计数构造函数
concurrent_flat_set(size_type n, allocator_type const& a);

使用 `hf` 作为哈希函数、默认哈希函数和键相等谓词以及 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。

后置条件

size() == 0

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


带哈希函数和分配器的桶计数构造函数
concurrent_flat_set(size_type n, hasher const& hf, allocator_type const& a);

使用 `hf` 作为哈希函数、默认键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。

后置条件

size() == 0

要求

`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();
注意

析构函数将应用于每个元素,并且所有内存都将被释放。


赋值

复制赋值
concurrent_flat_set& operator=(concurrent_flat_set const& other);

赋值运算符。销毁先前存在的元素,从other复制赋值哈希函数和谓词,如果Alloc::propagate_on_container_copy_assignment存在且Alloc::propagate_on_container_copy_assignment::valuetrue,则从other复制赋值分配器,最后插入other元素的副本。

要求

value_type可复制插入的。

并发性

阻塞于 `*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::valuetrue,则从other移动赋值分配器。如果此时分配器等于other.get_allocator(),则other的内部桶数组将直接转移到*this;否则,插入other元素的移动构造副本。如果启用了统计信息,则当且仅当最终分配器等于other.get_allocator()时才从other转移内部统计信息,并且始终调用other.reset_stats()

并发性

阻塞于 `*this` 和 `other`。


初始化列表赋值
concurrent_flat_set& operator=(std::initializer_list<value_type> il);

从初始化列表中赋值。所有先前存在的元素都将被销毁。

要求

value_type可复制插入的。

并发性

阻塞于 `*this`。


访问

[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以获得性能提升:超过此大小,性能预计不会进一步提高。

要求

FwdIterator是一个LegacyForwardIterator(C++11到C++17),或者满足std::forward_iterator(C++20及更高版本)。对于K = std::iterator_traits<FwdIterator>::value_typeK要么是key_type,要么Hash::is_transparentPred::is_transparent是有效的成员类型定义。在后一种情况下,库假设Hash可以同时使用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。

返回值

已访问元素的数量。


[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。根据指定的执行策略的语义并行执行。

抛出

根据所用执行策略的异常处理机制,如果在f中抛出异常,则可能会调用std::terminate

注释

仅在支持C++17并行算法的编译器中可用。

只有当std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>true时,这些重载才参与重载解析。

不允许无序执行策略。


[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或访问所有元素。

返回值

如果f曾经返回false,则为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或访问所有元素。根据指定的执行策略的语义并行执行。

返回值

如果f曾经返回false,则为false

抛出

根据所用执行策略的异常处理机制,如果在f中抛出异常,则可能会调用std::terminate

注释

仅在支持C++17并行算法的编译器中可用。

只有当std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>true时,这些重载才参与重载解析。

不允许无序执行策略。

并行化意味着执行并不一定在f返回false时立即结束,因此可能会使用返回值也为false的后续元素调用f


大小和容量

empty
[[nodiscard]] bool empty() const noexcept;
返回值

size() == 0


size
size_type size() const noexcept;
返回值

表中元素的数量。

注释

在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实大小。


max_size
size_type max_size() const noexcept;
返回值

最大可能表的size()


修改器

emplace
template<class... Args> bool emplace(Args&&... args);

仅当表中不存在具有等效键的元素时,才使用参数args构造的对象插入到表中。

要求

value_type 可由 args 构造。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。


复制插入
bool insert(const value_type& obj);

仅当表中不存在具有等效键的元素时,才将obj插入到表中。

要求

`value_type` 可 复制插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。


移动插入
bool insert(value_type&& obj);

仅当表中不存在具有等效键的元素时,才将obj插入到表中。

要求

`value_type` 可 移动插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。


透明插入
template<class K> bool insert(K&& k);

当且仅当容器中没有具有等效键的元素时,才将从std::forward<K>(k)构造的元素插入到容器中。

要求

value_type可以从kEmplaceConstructible构建。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,此重载才参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。


插入迭代器范围
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

要求

value_type 可由 args 构造。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。

该接口仅用于说明,因为C++不允许在可变参数包之后声明参数f


复制 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` 可 复制插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。


移动 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` 可 移动插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。


透明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

要求

value_type可以从kEmplaceConstructible构建。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,这些重载才参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。


插入迭代器范围或访问
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

要求

value_type 可由 args 构造。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。

该接口仅用于说明,因为C++不允许在可变参数包之后声明参数f1f2


复制 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` 可 复制插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。


移动 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` 可 移动插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。


透明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

要求

value_type可以从kEmplaceConstructible构建。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果发出重新哈希,则使指向元素的指针和引用无效。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,这些重载才参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。


插入迭代器范围和访问
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)。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


按键删除
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)。

抛出

只有当hasherkey_equalf抛出异常时,才会抛出异常。

注释

只有当std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>false时,template<class K, class F>重载才参与重载解析。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K, class F>重载才参与重载解析。库假设Hash可以同时使用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。


erase_if
template<class F> size_type erase_if(F f);

连续地使用对表中每个元素的引用调用f,并删除f返回true的那些元素。

返回值

删除的元素数量。

抛出

只有当f抛出异常时,才会抛出异常。


并行 erase_if
template<class ExecutionPolicy, class  F> void erase_if(ExecutionPolicy&& policy, F f);

使用对表中每个元素的引用调用f,并删除f返回true的那些元素。根据指定的执行策略的语义并行执行。

抛出

根据所用执行策略的异常处理机制,如果在f中抛出异常,则可能会调用std::terminate

注释

仅在支持C++17并行算法的编译器中可用。

只有当std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>true时,此重载才参与重载解析。

不允许无序执行策略。


交换 (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::valuetrue,则交换表的分配器。否则,使用不相等的分配器进行交换会导致未定义的行为。

抛出

除非 key_equalhasher 在交换时抛出异常。

并发性

阻塞于 `*this` 和 `other`。


清空 (clear)
void clear() noexcept;

删除表中的所有元素。

后置条件

size() == 0, max_load() >= max_load_factor() * bucket_count()

并发性

阻塞于 `*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 中删除这些元素。

返回值

插入的元素数量。

并发性

阻塞*thissource


观察者

获取分配器 (get_allocator)
allocator_type get_allocator() const noexcept;
返回值

表的分配器。


哈希函数 (hash_function)
hasher hash_function() const;
返回值

表的哈希函数。


键相等谓词 (key_eq)
key_equal key_eq() const;
返回值

表的键相等谓词。


集合操作

计数 (count)
size_type        count(const key_type& k) const;
template<class K>
  size_type      count(const K& k) const;
返回值

键与k等效的元素数量(0或1)。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。

在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。


包含 (contains)
bool             contains(const key_type& k) const;
template<class K>
  bool           contains(const K& k) const;
返回值

一个布尔值,指示表中是否存在键等于k的元素。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。

在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。


桶接口

桶数量 (bucket_count)
size_type bucket_count() const noexcept;
返回值

桶数组的大小。


哈希策略

负载因子 (load_factor)
float load_factor() const noexcept;
返回值

static_cast<float>(size())/static_cast<float>(bucket_count()),如果 bucket_count() == 0,则为 0


最大负载因子 (max_load_factor)
float max_load_factor() const noexcept;
返回值

返回表的最大负载因子。


设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果

不执行任何操作,因为不允许用户更改此参数。为了与 boost::unordered_set 保持兼容性而保留。


最大负载
size_type max_load() const noexcept;
返回值

在不重新哈希的情况下,表可以容纳的最大元素数量,假设不会再删除任何元素。

注意

构造、重新哈希或清除后,表的最大负载至少为max_load_factor() * bucket_count()。在高负载条件下,此数字可能会在删除时减少。

在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。


重新哈希 (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`。


统计

获取统计信息
stats get_stats() const;
返回值

到目前为止表执行的插入和查找操作的统计描述。

注释

仅当已启用统计计算时才可用。


重置统计信息
void reset_stats() noexcept;
效果

将表保留的内部统计信息设置为零。

注释

仅当已启用统计计算时才可用。


推导指南

如果出现以下任何情况,则推导指南不会参与重载解析:

  • 它具有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

并发性

阻塞xy

注释

如果两个表没有等效的相等谓词,则行为未定义。


不相等运算符 (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

并发性

阻塞xy

注释

如果两个表没有等效的相等谓词,则行为未定义。


交换

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中。

要求

value_type是可序列化的(XML可序列化的),并且它支持Boost.Serialization save_construct_data/load_construct_data协议(由DefaultConstructible类型自动支持)。

并发性

阻塞x


从存档加载concurrent_flat_set

删除concurrent_flat_set x中所有预先存在的元素,并从存档(XML存档)ar中插入原始concurrent_flat_set other元素的已恢复副本(这些元素已保存到ar读取的存储区中)。

要求

x.key_equal() 的功能等效于 other.key_equal()

并发性

阻塞x

类模板 concurrent_node_map

boost::concurrent_node_map — 一个基于节点的哈希表,它将唯一键与另一个值关联起来,并允许并发插入、删除、查找和访问元素,无需外部同步机制。

尽管它充当容器,但boost::concurrent_node_map并不符合标准C++ Container概念。特别是,不提供迭代器和相关操作(beginend等)。元素访问和修改是通过用户提供的_访问函数_来完成的,这些函数传递给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>>>;
  }
}

描述

模板参数

必须能够从任何可转换为它的std::pair对象将std::pair<const Key, T>就地构造到表中,并且还必须能够从表中删除它。

T

哈希

一个一元函数对象类型,充当 Key 的哈希函数。它接受一个类型为 Key 的参数,并返回一个类型为 std::size_t 的值。

Pred

一个二元函数对象,它在Key类型的数值上诱导等价关系。它接受两个Key类型的参数并返回一个bool类型的数值。

分配器

一个值类型与表的值类型相同的分配器。支持使用 花哨指针 的分配器。

表的元素节点保存在内部的_桶数组_中。节点插入到由其元素哈希码确定的桶中,但如果桶已被占用(_冲突_),则使用原始位置附近的一个可用桶。

桶数组的大小可以通过调用 `insert`/`emplace` 自动增加,或者通过调用 `rehash`/`reserve` 来增加。表的负载因子(元素数量除以桶的数量)永远不会大于 `max_load_factor()`,除非在较小的尺寸下,实现可能决定允许更高的负载。

如果hash_is_avalanching<Hash>::valuetrue,则哈希函数按原样使用;否则,会添加一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。


并发需求和保证

对同一 `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(其中xboost::concurrent_node_map的实例),则x上的先前阻塞操作将与op同步。因此,在多线程场景中,对同一concurrent_node_map的阻塞操作将顺序执行。

如果一个操作仅在发出内部重新哈希时才阻塞于 `x`,则称该操作为阻塞于 `x` 的重新哈希

当由boost::concurrent_node_map内部执行时,用户提供的访问函数对传递的元素执行以下操作不会引入数据竞争

  • 读取元素。

  • 非可变修改元素。

  • 可变修改元素。

    • 在接受两个访问函数的容器函数中,始终针对第一个函数。

    • 在名称不包含 `cvisit` 的非 const 容器函数中,针对最后一个(或唯一一个)访问函数。

任何插入或修改元素eboost::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` 发出信号。当运行时速度成为关注点时,可以通过全局定义此宏来禁用此功能。


BOOST_UNORDERED_ENABLE_STATS

全局定义此宏以启用表的 统计计算。请注意,此选项会降低许多操作的整体性能。


类型定义

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


常量

static constexpr size_type bulk_visit_size;

批量访问操作中内部使用的块大小。


构造函数

默认构造函数
concurrent_node_map();

使用 `hasher()` 作为哈希函数、`key_equal()` 作为键相等谓词和 `allocator_type()` 作为分配器构造一个空表。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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` 个桶的空表。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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());

使用 `hf` 作为哈希函数、`eql` 作为键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表,并将元素从 `[f, l)` 插入到其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


复制构造函数
concurrent_node_map(concurrent_node_map const& other);

复制构造函数。复制包含的元素、哈希函数、谓词和分配器。

如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。

要求

`value_type` 可复制构造。

并发性

阻塞other


移动构造函数
concurrent_node_map(concurrent_node_map&& other);

移动构造函数。other的内部桶数组直接转移到新表。哈希函数、谓词和分配器是从other移动构造的。如果已启用统计信息,则将内部统计信息从other转移并调用other.reset_stats()

并发性

阻塞other


带分配器的迭代器范围构造函数
template<class InputIterator>
  concurrent_node_map(InputIterator f, InputIterator l, const allocator_type& a);

使用 `a` 作为分配器,使用默认哈希函数和键相等谓词构造一个空表,并将元素从 `[f, l)` 插入到其中。

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


分配器构造函数
explicit concurrent_node_map(Allocator const& a);

使用分配器 `a` 构造一个空表。


带分配器的复制构造函数
concurrent_node_map(concurrent_node_map const& other, Allocator const& a);

构造一个表,复制 `other` 的包含元素、哈希函数和谓词,但使用分配器 `a`。

并发性

阻塞other


带分配器的移动构造函数
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()

并发性

阻塞other


从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(bucket_count())


初始化列表构造函数
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` 插入到其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


带分配器的桶计数构造函数
concurrent_node_map(size_type n, allocator_type const& a);

使用 `hf` 作为哈希函数、默认哈希函数和键相等谓词以及 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。

后置条件

size() == 0

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


带哈希函数和分配器的桶计数构造函数
concurrent_node_map(size_type n, hasher const& hf, allocator_type const& a);

使用 `hf` 作为哈希函数、默认键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。

后置条件

size() == 0

要求

`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();
注意

析构函数将应用于每个元素,并且所有内存都将被释放。


赋值

复制赋值
concurrent_node_map& operator=(concurrent_node_map const& other);

赋值运算符。销毁先前存在的元素,从other复制赋值哈希函数和谓词,如果Alloc::propagate_on_container_copy_assignment存在且Alloc::propagate_on_container_copy_assignment::valuetrue,则从other复制赋值分配器,最后插入other元素的副本。

要求

value_type可复制插入的。

并发性

阻塞于 `*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::valuetrue,则从other移动赋值分配器。如果此时分配器等于other.get_allocator(),则other的内部桶数组将直接转移到*this;否则,插入other元素的移动构造副本。如果已启用统计信息,则当且仅当最终分配器等于other.get_allocator()时,才将内部统计信息从other转移,并且始终调用other.reset_stats()

并发性

阻塞于 `*this` 和 `other`。


初始化列表赋值
concurrent_node_map& operator=(std::initializer_list<value_type> il);

从初始化列表中赋值。所有先前存在的元素都将被销毁。

要求

value_type可复制插入的。

并发性

阻塞于 `*this`。


访问

[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才能获得性能提升:超过此大小后,性能预计不会进一步提高。

要求

FwdIterator是一个LegacyForwardIterator(C++11到C++17),或者满足std::forward_iterator(C++20及更高版本)。对于K = std::iterator_traits<FwdIterator>::value_typeK要么是key_type,要么Hash::is_transparentPred::is_transparent是有效的成员类型定义。在后一种情况下,库假设Hash可以同时使用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。

返回值

已访问元素的数量。


[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是常量时,这些引用才是常量。根据指定的执行策略的语义并行执行。

抛出

根据所用执行策略的异常处理机制,如果在f中抛出异常,则可能会调用std::terminate

注释

仅在支持C++17并行算法的编译器中可用。

只有当std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>true时,这些重载才参与重载解析。

不允许无序执行策略。


[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是常量时,这些元素引用才是常量。

返回值

如果f曾经返回false,则为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或访问所有元素。只有当*this是常量时,这些元素引用才是常量。根据指定的执行策略的语义并行执行。

返回值

如果f曾经返回false,则为false

抛出

根据所用执行策略的异常处理机制,如果在f中抛出异常,则可能会调用std::terminate

注释

仅在支持C++17并行算法的编译器中可用。

只有当std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>true时,这些重载才参与重载解析。

不允许无序执行策略。

并行化意味着执行并不一定在f返回false时立即结束,因此可能会使用返回值也为false的后续元素调用f


大小和容量

empty
[[nodiscard]] bool empty() const noexcept;
返回值

size() == 0


size
size_type size() const noexcept;
返回值

表中元素的数量。

注释

在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实大小。


max_size
size_type max_size() const noexcept;
返回值

最大可能表的size()


修改器

emplace
template<class... Args> bool emplace(Args&&... args);

仅当表中不存在具有等效键的元素时,才使用参数args构造的对象插入到表中。

要求

value_type 可由 args 构造。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果 args…​ 的形式为 k,v,它会延迟构造整个对象,直到确定应该插入元素为止,只使用 k 参数进行检查。


复制插入
bool insert(const value_type& obj);
bool insert(const init_type& obj);

仅当表中不存在具有等效键的元素时,才将obj插入到表中。

要求

`value_type` 可 复制插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

x 可同时转换为 const value_type&const init_type& 时,调用 insert(x) 不会产生歧义,并选择 init_type 重载。


移动插入
bool insert(value_type&& obj);
bool insert(init_type&& obj);

仅当表中不存在具有等效键的元素时,才将obj插入到表中。

要求

`value_type` 可 移动插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

x 可同时转换为 value_type&&init_type&& 时,调用 insert(x) 不会产生歧义,并选择 init_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为空。

返回值

insertednode构造的insert_return_type对象

  • 如果nh为空,则insertedfalsenode为空。

  • 否则,如果插入成功,则inserted为true,node为空。

  • 如果插入失败,则inserted为false,node具有nh的先前值。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

并发性

阻塞*this的重新哈希。

注释

如果 nh 不为空且 nh 和容器的分配器不相等,则行为未定义。


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,则该引用为常量。

要求

value_type 可由 args 构造。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

该接口仅用于说明,因为C++不允许在可变参数包之后声明参数f


复制 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` 可 复制插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

insert_or_[c]visit(obj, f)形式的调用中,只有当std::remove_cv<std::remove_reference<decltype(obj)>::type>::typevalue_type时,接受const 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` 可 移动插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

insert_or_[c]visit(obj, f)形式的调用中,只有当std::remove_reference<decltype(obj)>::typevalue_type时,接受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。

返回值

insertednode构造的insert_return_type对象

  • 如果nh为空,则insertedfalsenode为空。

  • 否则,如果插入成功,则inserted为true,node为空。

  • 如果插入失败,则inserted为false,node具有nh的先前值。

抛出

如果操作(而不是对hasherf的调用)抛出异常,则该函数无效。

并发性

阻塞*this的重新哈希。

注释

如果 nh 不为空且 nh 和容器的分配器不相等,则行为未定义。


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,则该引用为常量。

要求

value_type 可由 args 构造。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

该接口仅用于说明,因为C++不允许在可变参数包之后声明参数f1f2


复制 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` 可 复制插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

insert_and_[c]visit(obj, f1, f2)形式的调用中,只有当std::remove_cv<std::remove_reference<decltype(obj)>::type>::typevalue_type时,接受const 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` 可 移动插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

insert_and_[c]visit(obj, f1, f2)形式的调用中,只有当std::remove_reference<decltype(obj)>::typevalue_type时,接受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。

返回值

insertednode构造的insert_return_type对象

  • 如果nh为空,则insertedfalsenode为空。

  • 否则,如果插入成功,则inserted为true,node为空。

  • 如果插入失败,则inserted为false,node具有nh的先前值。

抛出

如果操作(而不是对hasherf1f2的调用)抛出异常,则该函数无效。

并发性

阻塞*this的重新哈希。

注释

如果 nh 不为空且 nh 和容器的分配器不相等,则行为未定义。


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的现有元素,则将使用kargs构造的元素插入到表中。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

此函数类似于emplace,不同之处在于,如果存在具有等效键的元素,则不构造value_type;否则,构造形式为

// first two overloads
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<Key>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

// third overload
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<K>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

emplace不同,后者只是将所有参数转发到value_type的构造函数。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K, class... Args>重载才参与重载解析。库假设Hash可以同时使用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。


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的现有元素,则将使用kargs构造的元素插入到表中。否则,使用对等效元素的引用调用f;只有当使用*_cvisit重载时,该引用才是常量。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果存在具有等效键的元素,则不会构造value_type;否则,构造形式为

// first four overloads
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<Key>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

// last two overloads
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<K>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

该接口仅用于说明,因为C++不允许在可变参数包之后声明参数f

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K, class... Args, class F>重载才参与重载解析。库假设Hash可以同时使用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。


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的现有元素,则将使用kargs构造的元素插入到表中,然后使用对新创建元素的非常量引用调用f1。否则,使用对等效元素的引用调用f2;只有当使用*_cvisit重载时,该引用才是常量。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

如果存在具有等效键的元素,则不会构造value_type;否则,构造形式为

// first four overloads
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<Key>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

// last two overloads
value_type(std::piecewise_construct,
           std::forward_as_tuple(std::forward<K>(k)),
           std::forward_as_tuple(std::forward<Args>(args)...))

该接口仅供说明,因为C++不允许在可变参数包之后声明参数f1f2

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K, class... Args, class F1, class F2>重载才参与重载解析。库假设Hash可以同时使用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。


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)))
返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

`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)。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


按键删除
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)。

抛出

只有当hasherkey_equalf抛出异常时,才会抛出异常。

注释

只有当std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>false时,template<class K, class F>重载才参与重载解析。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K, class F>重载才参与重载解析。库假设Hash可以同时使用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。


erase_if
template<class F> size_type erase_if(F f);

连续地使用对表中每个元素的引用调用f,并删除f返回true的那些元素。

返回值

删除的元素数量。

抛出

只有当f抛出异常时,才会抛出异常。


并行 erase_if
template<class ExecutionPolicy, class  F> void erase_if(ExecutionPolicy&& policy, F f);

使用对表中每个元素的引用调用f,并删除f返回true的那些元素。根据指定的执行策略的语义并行执行。

抛出

根据所用执行策略的异常处理机制,如果在f中抛出异常,则可能会调用std::terminate

注释

仅在支持C++17并行算法的编译器中可用。

只有当std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>true时,此重载才参与重载解析。

不允许无序执行策略。


交换 (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::valuetrue,则交换表的分配器。否则,使用不相等的分配器进行交换会导致未定义的行为。

抛出

除非 key_equalhasher 在交换时抛出异常。

并发性

阻塞于 `*this` 和 `other`。


提取
node_type extract(const key_type& k);
template<class K> node_type extract(K&& k);

提取键与 k 等效的元素(如果存在)。

返回值

包含提取元素的 node_type 对象,如果未提取任何元素,则为空。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


条件提取
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,则提取该元素。

返回值

包含提取元素的 node_type 对象,如果未提取任何元素,则为空。

抛出

仅当hasherkey_equalf抛出异常时,才抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


清空 (clear)
void clear() noexcept;

删除表中的所有元素。

后置条件

size() == 0, max_load() >= max_load_factor() * bucket_count()

并发性

阻塞于 `*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 中删除这些元素。

返回值

插入的元素数量。

并发性

阻塞*thissource


观察者

获取分配器 (get_allocator)
allocator_type get_allocator() const noexcept;
返回值

表的分配器。


哈希函数 (hash_function)
hasher hash_function() const;
返回值

表的哈希函数。


键相等谓词 (key_eq)
key_equal key_eq() const;
返回值

表的键相等谓词。


映射操作

计数 (count)
size_type        count(const key_type& k) const;
template<class K>
  size_type      count(const K& k) const;
返回值

键与k等效的元素数量(0或1)。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。

在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。


包含 (contains)
bool             contains(const key_type& k) const;
template<class K>
  bool           contains(const K& k) const;
返回值

一个布尔值,指示表中是否存在键等于k的元素。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。

在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。


桶接口

桶数量 (bucket_count)
size_type bucket_count() const noexcept;
返回值

桶数组的大小。


哈希策略

负载因子 (load_factor)
float load_factor() const noexcept;
返回值

static_cast<float>(size())/static_cast<float>(bucket_count()),如果 bucket_count() == 0,则为 0


最大负载因子 (max_load_factor)
float max_load_factor() const noexcept;
返回值

返回表的最大负载因子。


设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果

不执行任何操作,因为不允许用户更改此参数。为了与 boost::unordered_map 保持兼容性而保留。


最大负载
size_type max_load() const noexcept;
返回值

在不重新哈希的情况下,表可以容纳的最大元素数量,假设不会再删除任何元素。

注意

构造、重新哈希或清除后,表的最大负载至少为max_load_factor() * bucket_count()。在高负载条件下,此数字可能会在删除时减少。

在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。


重新哈希 (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`。


统计

获取统计信息
stats get_stats() const;
返回值

到目前为止表执行的插入和查找操作的统计描述。

注释

仅当已启用统计计算时才可用。


重置统计信息
void reset_stats() noexcept;
效果

将表保留的内部统计信息设置为零。

注释

仅当已启用统计计算时才可用。


推导指南

如果出现以下任何情况,则推导指南不会参与重载解析:

  • 它具有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

并发性

阻塞xy

注释

如果两个表没有等效的相等谓词,则行为未定义。


不相等运算符 (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

并发性

阻塞xy

注释

如果两个表没有等效的相等谓词,则行为未定义。


交换

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中。

要求

std::remove_const<key_type>::typestd::remove_const<mapped_type>::type是可序列化的(XML可序列化的),并且它们支持Boost.Serialization save_construct_data/load_construct_data协议(由DefaultConstructible类型自动支持)。

并发性

阻塞x


从存档加载concurrent_node_map

删除concurrent_node_map x中所有预先存在的元素,并从存档(XML存档)ar中插入原始concurrent_node_map other元素的已恢复副本(这些元素已保存到ar读取的存储区中)。

要求

x.key_equal() 的功能等效于 other.key_equal()

并发性

阻塞x

类模板 concurrent_node_set

boost::concurrent_node_set — 一个基于节点的哈希表,它存储唯一值,并允许并发插入、删除、查找和访问元素,无需外部同步机制。

尽管它充当容器,但boost::concurrent_node_set并不符合标准C++ Container概念。特别是,不提供迭代器和相关操作(beginend等)。元素访问是通过用户提供的_访问函数_来完成的,这些函数传递给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>>;
  }
}

描述

模板参数

Key 必须可以移动插入 到容器中,并可以从容器中删除

哈希

一个一元函数对象类型,充当 Key 的哈希函数。它接受一个类型为 Key 的参数,并返回一个类型为 std::size_t 的值。

Pred

一个二元函数对象,它在Key类型的数值上诱导等价关系。它接受两个Key类型的参数并返回一个bool类型的数值。

分配器

其值类型与表的值类型相同的分配器。std::allocator_traits<Allocator>::pointerstd::allocator_traits<Allocator>::const_pointer必须分别可转换为/从value_type*const value_type*

表的元素节点保存在内部的_桶数组_中。节点插入到由其元素哈希码确定的桶中,但如果桶已被占用(_冲突_),则使用原始位置附近的一个可用桶。

桶数组的大小可以通过调用 `insert`/`emplace` 自动增加,或者通过调用 `rehash`/`reserve` 来增加。表的负载因子(元素数量除以桶的数量)永远不会大于 `max_load_factor()`,除非在较小的尺寸下,实现可能决定允许更高的负载。

如果hash_is_avalanching<Hash>::valuetrue,则哈希函数按原样使用;否则,会添加一个位混合后处理阶段,以提高哈希质量,但会增加额外的计算成本。


并发需求和保证

对同一 `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(其中xboost::concurrent_node_set的实例),则x上的先前阻塞操作将与op同步。因此,在多线程场景中,对同一concurrent_node_set的阻塞操作将顺序执行。

如果一个操作仅在发出内部重新哈希时才阻塞于 `x`,则称该操作为阻塞于 `x` 的重新哈希

当由boost::concurrent_node_set内部执行时,用户提供的访问函数对传递的元素执行以下操作不会引入数据竞争

  • 读取元素。

  • 非可变修改元素。

  • 可变修改元素。

    • 在接受两个访问函数的容器函数中,始终针对第一个函数。

    • 在名称不包含 `cvisit` 的非 const 容器函数中,针对最后一个(或唯一一个)访问函数。

任何插入或修改元素eboost::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` 发出信号。当运行时速度成为关注点时,可以通过全局定义此宏来禁用此功能。


BOOST_UNORDERED_ENABLE_STATS

全局定义此宏以启用表的 统计计算。请注意,此选项会降低许多操作的整体性能。


类型定义

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


常量

static constexpr size_type bulk_visit_size;

批量访问操作中内部使用的块大小。

构造函数

默认构造函数
concurrent_node_set();

使用 `hasher()` 作为哈希函数、`key_equal()` 作为键相等谓词和 `allocator_type()` 作为分配器构造一个空表。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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` 个桶的空表。

后置条件

size() == 0

要求

如果使用默认值,则 hasherkey_equalallocator_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());

使用 `hf` 作为哈希函数、`eql` 作为键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表,并将元素从 `[f, l)` 插入到其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


复制构造函数
concurrent_node_set(concurrent_node_set const& other);

复制构造函数。复制包含的元素、哈希函数、谓词和分配器。

如果存在 `Allocator::select_on_container_copy_construction` 且具有正确的签名,则将从其结果构造分配器。

要求

`value_type` 可复制构造。

并发性

阻塞other


移动构造函数
concurrent_node_set(concurrent_node_set&& other);

移动构造函数。other 的内部桶数组直接转移到新表。哈希函数、谓词和分配器从 other 移动构造。如果启用了统计信息 enabled,则从 other 转移内部统计信息并调用 other.reset_stats()

并发性

阻塞other


带分配器的迭代器范围构造函数
template<class InputIterator>
  concurrent_node_set(InputIterator f, InputIterator l, const allocator_type& a);

使用 `a` 作为分配器,使用默认哈希函数和键相等谓词构造一个空表,并将元素从 `[f, l)` 插入到其中。

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


分配器构造函数
explicit concurrent_node_set(Allocator const& a);

使用分配器 `a` 构造一个空表。


带分配器的复制构造函数
concurrent_node_set(concurrent_node_set const& other, Allocator const& a);

构造一个表,复制 `other` 的包含元素、哈希函数和谓词,但使用分配器 `a`。

并发性

阻塞other


带分配器的移动构造函数
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()

并发性

阻塞other


从 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(bucket_count())


初始化列表构造函数
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` 插入到其中。

要求

如果使用默认值,则 hasherkey_equalallocator_type 需要是默认可构造的


带分配器的桶计数构造函数
concurrent_node_set(size_type n, allocator_type const& a);

使用 `hf` 作为哈希函数、默认哈希函数和键相等谓词以及 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。

后置条件

size() == 0

要求

`hasher` 和 `key_equal` 需要是 可默认构造的


带哈希函数和分配器的桶计数构造函数
concurrent_node_set(size_type n, hasher const& hf, allocator_type const& a);

使用 `hf` 作为哈希函数、默认键相等谓词和 `a` 作为分配器构造一个至少具有 `n` 个桶的空表。

后置条件

size() == 0

要求

`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();
注意

析构函数将应用于每个元素,并且所有内存都将被释放。


赋值

复制赋值
concurrent_node_set& operator=(concurrent_node_set const& other);

赋值运算符。销毁先前存在的元素,从other复制赋值哈希函数和谓词,如果Alloc::propagate_on_container_copy_assignment存在且Alloc::propagate_on_container_copy_assignment::valuetrue,则从other复制赋值分配器,最后插入other元素的副本。

要求

value_type可复制插入的。

并发性

阻塞于 `*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_assignmentAlloc::propagate_on_container_move_assignment::valuetrue,则从 other 移动赋值分配器。如果此时分配器等于 other.get_allocator(),则 other 的内部桶数组直接转移到 *this;否则,插入 other 元素的移动构造副本。如果启用了统计信息 enabled,则当且仅当最终分配器等于 other.get_allocator() 时转移内部统计信息,并且始终调用 other.reset_stats()

并发性

阻塞于 `*this` 和 `other`。


初始化列表赋值
concurrent_node_set& operator=(std::initializer_list<value_type> il);

从初始化列表中赋值。所有先前存在的元素都将被销毁。

要求

value_type可复制插入的。

并发性

阻塞于 `*this`。


访问

[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 以获得性能提升:超过此大小,性能预计不会进一步提高。

要求

FwdIterator是一个LegacyForwardIterator(C++11到C++17),或者满足std::forward_iterator(C++20及更高版本)。对于K = std::iterator_traits<FwdIterator>::value_typeK要么是key_type,要么Hash::is_transparentPred::is_transparent是有效的成员类型定义。在后一种情况下,库假设Hash可以同时使用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。

返回值

已访问元素的数量。


[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。根据指定的执行策略的语义并行执行。

抛出

根据所用执行策略的异常处理机制,如果在f中抛出异常,则可能会调用std::terminate

注释

仅在支持C++17并行算法的编译器中可用。

只有当std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>true时,这些重载才参与重载解析。

不允许无序执行策略。


[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或访问所有元素。

返回值

如果f曾经返回false,则为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或访问所有元素。根据指定的执行策略的语义并行执行。

返回值

如果f曾经返回false,则为false

抛出

根据所用执行策略的异常处理机制,如果在f中抛出异常,则可能会调用std::terminate

注释

仅在支持C++17并行算法的编译器中可用。

只有当std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>true时,这些重载才参与重载解析。

不允许无序执行策略。

并行化意味着执行并不一定在f返回false时立即结束,因此可能会使用返回值也为false的后续元素调用f


大小和容量

empty
[[nodiscard]] bool empty() const noexcept;
返回值

size() == 0


size
size_type size() const noexcept;
返回值

表中元素的数量。

注释

在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实大小。


max_size
size_type max_size() const noexcept;
返回值

最大可能表的size()


修改器

emplace
template<class... Args> bool emplace(Args&&... args);

仅当表中不存在具有等效键的元素时,才使用参数args构造的对象插入到表中。

要求

value_type 可由 args 构造。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。


复制插入
bool insert(const value_type& obj);

仅当表中不存在具有等效键的元素时,才将obj插入到表中。

要求

`value_type` 可 复制插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。


移动插入
bool insert(value_type&& obj);

仅当表中不存在具有等效键的元素时,才将obj插入到表中。

要求

`value_type` 可 移动插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。


透明插入
template<class K> bool insert(K&& k);

当且仅当容器中没有具有等效键的元素时,才将从std::forward<K>(k)构造的元素插入到容器中。

要求

value_type可以从kEmplaceConstructible构建。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,此重载才参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。


插入迭代器范围
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 为空。

返回值

insertednode构造的insert_return_type对象

  • 如果nh为空,则insertedfalsenode为空。

  • 否则,如果插入成功,则inserted为true,node为空。

  • 如果插入失败,则inserted为false,node具有nh的先前值。

抛出

如果除对 `hasher` 的调用之外的操作抛出异常,则函数无效。

并发性

阻塞*this的重新哈希。

注释

如果 nh 不为空且 nh 和容器的分配器不相等,则行为未定义。


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

要求

value_type 可由 args 构造。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

该接口仅用于说明,因为C++不允许在可变参数包之后声明参数f


复制 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` 可 复制插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。


移动 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` 可 移动插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。


透明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

要求

value_type可以从kEmplaceConstructible构建。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,这些重载才参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。


插入迭代器范围或访问
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

返回值

insertednode构造的insert_return_type对象

  • 如果nh为空,则insertedfalsenode为空。

  • 否则,如果插入成功,则inserted为true,node为空。

  • 如果插入失败,则inserted为false,node具有nh的先前值。

抛出

如果操作(而不是对hasherf的调用)抛出异常,则该函数无效。

并发性

阻塞*this的重新哈希。

注释

如果 nh 不为空且 nh 和容器的分配器不相等,则行为未定义。


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

要求

value_type 可由 args 构造。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

该接口仅用于说明,因为C++不允许在可变参数包之后声明参数f1f2


复制 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` 可 复制插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。


移动 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` 可 移动插入

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。


透明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

要求

value_type可以从kEmplaceConstructible构建。

返回值

如果进行了插入,则为true

并发性

阻塞*this的重新哈希。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,这些重载才参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。


插入迭代器范围和访问
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

返回值

insertednode构造的insert_return_type对象

  • 如果nh为空,则insertedfalsenode为空。

  • 否则,如果插入成功,则inserted为true,node为空。

  • 如果插入失败,则inserted为false,node具有nh的先前值。

抛出

如果操作(而不是对hasherf1f2的调用)抛出异常,则该函数无效。

并发性

阻塞*this的重新哈希。

注释

如果 nh 不为空且 nh 和容器的分配器不相等,则行为未定义。


erase
size_type erase(const key_type& k);
template<class K> size_type erase(const K& k);

如果存在,则删除键与k等效的元素。

返回值

删除的元素数量(0或1)。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


按键删除
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)。

抛出

只有当hasherkey_equalf抛出异常时,才会抛出异常。

注释

只有当std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>false时,template<class K, class F>重载才参与重载解析。

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K, class F>重载才参与重载解析。库假设Hash可以同时使用KKey调用,并且Pred是透明的。这使得异构查找能够避免实例化Key类型实例的成本。


erase_if
template<class F> size_type erase_if(F f);

连续地使用对表中每个元素的引用调用f,并删除f返回true的那些元素。

返回值

删除的元素数量。

抛出

只有当f抛出异常时,才会抛出异常。


并行 erase_if
template<class ExecutionPolicy, class  F> void erase_if(ExecutionPolicy&& policy, F f);

使用对表中每个元素的引用调用f,并删除f返回true的那些元素。根据指定的执行策略的语义并行执行。

抛出

根据所用执行策略的异常处理机制,如果在f中抛出异常,则可能会调用std::terminate

注释

仅在支持C++17并行算法的编译器中可用。

只有当std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>true时,此重载才参与重载解析。

不允许无序执行策略。


交换 (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::valuetrue,则交换表的分配器。否则,使用不相等的分配器进行交换会导致未定义的行为。

抛出

除非 key_equalhasher 在交换时抛出异常。

并发性

阻塞于 `*this` 和 `other`。


提取
node_type extract(const key_type& k);
template<class K> node_type extract(K&& k);

提取键与 k 等效的元素(如果存在)。

返回值

包含提取元素的 node_type 对象,如果未提取任何元素,则为空。

抛出

只有当hasherkey_equal抛出异常时,才会抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


条件提取
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,则提取该元素。

返回值

包含提取元素的 node_type 对象,如果未提取任何元素,则为空。

抛出

仅当hasherkey_equalf抛出异常时,才抛出异常。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。


清空 (clear)
void clear() noexcept;

删除表中的所有元素。

后置条件

size() == 0, max_load() >= max_load_factor() * bucket_count()

并发性

阻塞于 `*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 中删除这些元素。

返回值

插入的元素数量。

并发性

阻塞*thissource


观察者

获取分配器 (get_allocator)
allocator_type get_allocator() const noexcept;
返回值

表的分配器。


哈希函数 (hash_function)
hasher hash_function() const;
返回值

表的哈希函数。


键相等谓词 (key_eq)
key_equal key_eq() const;
返回值

表的键相等谓词。


集合操作

计数 (count)
size_type        count(const key_type& k) const;
template<class K>
  size_type      count(const K& k) const;
返回值

键与k等效的元素数量(0或1)。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。

在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。


包含 (contains)
bool             contains(const key_type& k) const;
template<class K>
  bool           contains(const K& k) const;
返回值

一个布尔值,指示表中是否存在键等于k的元素。

注释

只有当Hash::is_transparentPred::is_transparent是有效的成员类型定义时,template<class K>重载才会参与重载解析。库假设Hash可以用KKey调用,并且Pred是透明的。这使得异构查找成为可能,避免了实例化Key类型实例的成本。

在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。


桶接口

桶数量 (bucket_count)
size_type bucket_count() const noexcept;
返回值

桶数组的大小。


哈希策略

负载因子 (load_factor)
float load_factor() const noexcept;
返回值

static_cast<float>(size())/static_cast<float>(bucket_count()),如果 bucket_count() == 0,则为 0


最大负载因子 (max_load_factor)
float max_load_factor() const noexcept;
返回值

返回表的最大负载因子。


设置最大负载因子 (Set max_load_factor)
void max_load_factor(float z);
效果

不执行任何操作,因为不允许用户更改此参数。为了与 boost::unordered_set 保持兼容性而保留。


最大负载
size_type max_load() const noexcept;
返回值

在不重新哈希的情况下,表可以容纳的最大元素数量,假设不会再删除任何元素。

注意

构造、重新哈希或清除后,表的最大负载至少为max_load_factor() * bucket_count()。在高负载条件下,此数字可能会在删除时减少。

在并发插入操作的情况下,返回值可能无法准确反映执行后表的真实状态。


重新哈希 (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`。


统计

获取统计信息
stats get_stats() const;
返回值

到目前为止表执行的插入和查找操作的统计描述。

注释

仅当启用了 统计信息计算 enabled 时才可用。


重置统计信息
void reset_stats() noexcept;
效果

将表保留的内部统计信息设置为零。

注释

仅当启用了 统计信息计算 enabled 时才可用。


推导指南

如果出现以下任何情况,则推导指南不会参与重载解析:

  • 它具有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

并发性

阻塞xy

注释

如果两个表没有等效的相等谓词,则行为未定义。


不相等运算符 (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

并发性

阻塞xy

注释

如果两个表没有等效的相等谓词,则行为未定义。


交换

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 中。

要求

value_type是可序列化的(XML可序列化的),并且它支持Boost.Serialization save_construct_data/load_construct_data协议(由DefaultConstructible类型自动支持)。

并发性

阻塞x


从存档加载 concurrent_node_set

删除 concurrent_node_set x 中所有预先存在的元素,并从存档(XML 存档)ar 中插入原始 concurrent_node_set other 元素的已恢复副本(这些副本已保存到 ar 读取的存储中)。

要求

x.key_equal() 的功能等效于 other.key_equal()

并发性

阻塞x

变更日志

1.87.0 版本 - 主要更新

  • 添加了基于并发节点的容器 boost::concurrent_node_mapboost::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]visitstd::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.85.0 版本

  • 优化了 emplace() 以用于 value_typeinit_type(如果适用)参数,以绕过创建中间对象。该参数已与将要成为中间对象的类型相同。

  • 优化了映射容器上 k,v 参数的 emplace(),以延迟构造对象,直到确定应插入元素为止。当映射的 key_type 可移动构造或 k 参数为 key_type 时,会发生此优化。

  • 修复了对具有 explicit 复制构造函数的分配器的支持 (PR#234)。

  • 修复了 unordered_multimap::find(k, hash, eq)const 版本中的错误 (PR#238)。

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]setboost::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_mapboost::unordered_node_set

  • 根据 P2363 将异构查找扩展到更多成员函数。

  • 将开放寻址容器以前的混合后处理过程替换为基于常数扩展乘法的新的算法。

  • 修复了内部 emplace() 实现中的错误,其中堆栈局部类型未使用容器的分配器正确构造,这会破坏使用分配器构造。

1.81.0 版本 - 主要更新

  • 添加了基于开放寻址的快速容器 boost::unordered_flat_mapboost::unordered_flat_set

  • 为所有容器添加了 CTAD 推导指南。

  • 添加了 LWG issue 2713 中指定的缺失构造函数。

1.80.0 版本 - 主要更新

  • 重构内部实现,使其速度大幅提升

  • 允许使用 final Hasher 和 KeyEqual 对象

  • 更新文档,添加基准图和关于新的内部数据结构的说明

1.79.0 版本

  • 改进了 C++20 支持

    • 所有容器都已更新为支持异构 countequal_rangefind

    • 所有容器现在都实现了成员函数 contains

    • 已为所有容器实现 erase_if

  • 改进了 C++23 支持

    • 所有容器都已更新为支持异构 eraseextract

  • 更改了 reserve 的行为,使其积极分配 (PR#59)。

  • 测试套件中的各种警告修复。

  • 更新代码以内部使用 boost::allocator_traits

  • 切换到斐波那契散列。

  • 更新文档,使用 AsciiDoc 代替 QuickBook 编写。

1.67.0 版本

  • 改进了 C++17 支持

    • 添加标准中的模板推导指南。

    • 在节点句柄中使用简单的 optional 实现,使它们更接近标准。

    • swapoperator= 和节点句柄添加缺失的 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.66.0 版本

  • 更简单的移动构造实现。

  • 文档修正 (GitHub #6).

1.65.0 版本

  • quick_eraseerase_return_void添加弃用属性。这次我确实会在未来的版本中移除它们。

  • 少量标准兼容性修正

    • swap自由函数的noexpect规范。

    • 添加缺失的insert(P&&)方法。

1.64.0 版本

  • 初步支持新的C++17成员函数:unordered_map中的insert_or_assigntry_emplace

  • 初步支持mergeextract。目前还不包括在unordered_mapunordered_multimap之间或在unordered_setunordered_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_multisetunordered_multimap的异常安全问题。可能会稍微慢一些。

  • 停止使用某些旧编译器存在问题的返回值SFINAE。

1.58.0 版本

  • 从const迭代器中移除不必要的模板参数。

  • 重命名某些迭代器类中的私有iterator typedef,因为它会使某些traits类混淆。

  • 修复具有状态的、propagate_on_container_move_assign分配器的移动赋值 (#10777).

  • 修复移动赋值中罕见的异常安全问题。

  • 修复计算要分配的桶数时潜在的溢出问题 (GitHub #4).

1.57.0 版本

  • 修复迭代器中的pointer typedef (#10672).

  • 修复Coverity警告 (GitHub #2).

1.56.0 版本

  • 修复一些隐藏变量警告 (#9377).

  • 修复文档中的分配器使用 (#9719).

  • 始终对整数使用素数个桶。修复了插入连续整数时的性能回归,尽管使其他用途变慢了 (#9282).

  • 仅使用分配器构造元素,如C++11标准中所指定。

1.55.0 版本

  • 避免一些警告 (#8851, #8874).

  • 避免通过迭代器上的ADL公开一些细节函数。

  • 遵循标准,仅使用分配器的构造和销毁方法来构造和销毁存储的元素。不要将它们用于内部数据,例如指针。

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.51.0 版本

  • 修复使用C++11编译器和C++03分配器时的构造/析构问题 (#7100).

  • 移除try..catch以支持无需异常进行编译。

  • 调整SFINAE的使用以尝试支持g++ 3.4 (#7175).

  • 更新为使用新的配置宏。

1.50.0 版本

  • 修复unordered_multisetunordered_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_eraseerase_return_void不再需要,它们将在未来的版本中移除。

C++11支持导致了一些重大更改

  • 相等比较已更改为C++11规范。在具有等效键的容器中,具有相等键的组中的元素以前必须具有相同的顺序才能被视为相等,现在它们可以是彼此的排列。要使用旧的行为,请定义宏BOOST_UNORDERED_DEPRECATED_EQUALITY

  • 当要交换的两个容器具有不相等的分配器时,交换的行为有所不同。它以前使用适当的分配器分配新的节点,现在如果分配器具有成员结构propagate_on_container_swap,并且propagate_on_container_swap::value为true,则交换分配器。

  • 分配器的constructdestroy函数使用原始指针调用,而不是分配器的pointer类型。

  • emplace以前模拟了出现在早期C++0x草案中的可变参数对构造函数。由于它们已被移除,它不再这样做。它确实模拟了新的piecewise_construct对构造函数——只需要使用boost::piecewise_construct。要使用旧的可变参数构造函数模拟,请定义BOOST_UNORDERED_DEPRECATED_PAIR_CONSTRUCT

1.45.0 版本

  • 修复使用返回按复制方式返回value_type的迭代器插入unordered_mapunordered_set时的错误。

1.43.0 版本

  • 工单 3966erase_return_void现在是quick_erase,它是解决通过迭代器缓慢擦除的当前前兆,尽管将来很可能会有所改变。旧的方法名称保留是为了向后兼容性,但它被认为已弃用,并将在此后的版本中删除。

  • 使用Boost.Exception。

  • 停止使用已弃用的BOOST_HAS_*宏。

1.42.0 版本

  • 支持使用不完整的value类型实例化容器。

  • 减少了警告的数量(主要是在测试中)。

  • 改进了CodeGear兼容性。

  • 工单 3693:添加erase_return_void作为当前erase的临时解决方法,因为它可能效率低下,因为它必须查找下一个元素才能返回迭代器。

  • 添加用于兼容键的模板化查找重载。

  • 工单 3773:向ptrdiff_t添加缺失的std限定符。

  • 一些代码格式更改,以使几乎所有行都适合80个字符。

1.41.0 版本 - 主要更新

  • 原始版本大量使用宏来规避一些旧编译器较差的模板支持。但是,由于我不再支持这些编译器,并且宏的使用开始成为维护负担,因此它已被重写为使用模板而不是宏来实现类。

  • 由于对EBO使用boost::compressed_pair和略微不同的函数缓冲区(现在使用bool而不是成员指针),容器对象现在更小。

  • 桶是延迟分配的,这意味着构造空容器不会分配任何内存。

1.40.0 版本

  • 工单 2975:将素数列表存储为预处理器序列 - 这样如果将来再次更改,它将始终获得正确的长度。

  • 工单 1978:为所有编译器实现emplace

  • 工单 2908工单 3096:针对旧版本的Borland的一些解决方法,包括向所有容器添加显式析构函数。

  • 工单 3082:禁用不正确的Visual C++警告。

  • 当头文件不可用时,C++0x特性的更好配置。

  • 默认情况下创建更少的桶。

1.39.0 版本

  • 工单 2756:避免Visual C++ 2009中的警告。

  • 对实现、测试和文档的一些其他次要内部更改。

  • 避免operator[]中的不必要复制。

  • 工单 2975:修复素数列表的长度。

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日进行的评审。

参考文献

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)