26 容器库
译注:翻译进行中。部分过长表格将在翻译完成后修订。将考虑构建英文修订版本。
26.1 简介
- 1 本节描述C++程序中可以用来组织信息收集的组件。
- 2 下面几节描述了容器的要求,以及序列容器和关联容器的组件,总结在表82中。
| 子章节 | 头文件 |
|---|---|
| 26.2 要求 | |
| 26.3 序列容器 | <array><deque><forward_list><list><vector> |
| 26.4 关联容器 | <map><set> |
| 26.5 无序关联容器 | <unordered_map><unordered_set> |
| 26.6 容器适配器 | <queue><stack> |
26.2 容器要求
26.2.1 容器要求简介
- 1 容器是存储其它对象的对象。它们通过构造函数、析构函数、插入和删除操作来控制这些对象的分配和释放。
- 2 本节中所有的复杂度要求仅以包含对象的操作数表示。【例:类型
vector<vector<int>>的构造函数有线性复杂度,即使拷贝每个包含的vector<int>的复杂度本身就是线性的。——例结束】 - 3 本节中的组件都声明了一个
allocator_type,存储在这些组件中的对象应使用allocator_traits<allocator_type>::rebind_traits<U>::construct函数构造并使用allocator_traits<allocator_type>::rebind_traits<U>::destroy函数销毁(23.10.8.2),且U是allocator_type::value_type或一种容器使用的内置类型。这些函数仅为容器的元素类型调用,不为容器使用的内置类型调用。【注:这意味着,例如,基于节点的容器可能需要构造包含对齐缓冲区的节点,并调用construct将元素放置到缓冲区中。——注结束】 - 4 在表83、84和85中,
X表示一个包含对象类型为T的容器类,a和b表示类型为X的值,u表示一个标识符,r表示类型X的一个非常量值,rv表示类型X的一个非常量右值。
| 表达式 | 返回类型 | 操作 语义 | 断言/说明 前置/后置条件 | 复杂度 |
|---|---|---|---|---|
X::value_type | T | 要求:T从X中是Erasable(见26.2.1下方) | 编译时 | |
X::reference | T& | 编译时 | ||
X::const_reference | const T& | 编译时 | ||
X::iterator | 值类型为T的迭代器类型 | 任何满足前向迭代器的迭代器类别。 可转换为 X::const_iterator。 | 编译时 | |
X::const_iterator | 值类型为T的常量迭代器类型 | 任何满足前向迭代器的迭代器类别。 | 编译时 | |
X::difference_type | 有符号整数类型 | 与X::iterator和X::const_iterator的差距类型相同 | 编译时 | |
X::size_type | 无符号整数类型 | size_type能表示difference_type的任意非负值 | 编译时 | |
X u; | 后置条件:u.empty() | 常量 | ||
X() | 后置条件:X().empty() | 常量 | ||
X(a) | 要求:T到X中CopyInsertable(见下)。后置条件: a == X(a) | 线性 | ||
X u(a);X u = a; | 要求:T到X中CopyInsertable(见下)。后置条件: u == a | 线性 | ||
X u(rv);X u = rv; | 后置条件:u应与在此次构造前rv拥有的值相等 | (说明B) | ||
a = rv | X& | a存在的所有元素被移动赋值或销毁 | a应与在此次赋值前rv拥有的值相等 | 线性 |
(&a)->~X() | void | 析构函数被应用于a的每一个元素;任何获得的内存被释放 | 线性 | |
a.begin() | iterator;对常量a是const_iterator | 常量 | ||
a.end() | iterator;对常量a是const_iterator | 常量 | ||
a.cbegin() | const_iterator | const_cast<X const&>(a).begin(); | 常量 | |
a.cend() | const_iterator | const_cast<X const&>(a).end(); | 常量 | |
a == b | 可转换为bool | ==是一种相等关系。equal(a.begin(),a.end(),b.begin(),b.end()) | 要求:T为EqualityComparable | 若a.size()!= b.size()则为常量,否则为线性 |
a != b | 可转换为bool | 相当于!(a == b) | 线性 | |
a.swap(b) | void | 交换a和b的内容 | (说明A) | |
swap(a, b) | void | a.swap(b) | (说明A) | |
r = a | X& | 后置条件:r == a | 线性 | |
a.size() | size_type | distance(a.begin(),a.end()) | 常量 | |
a.max_size() | size_type | 对最大可能容器的distance(a.begin(),a.end()) | 常量 | |
a.empty() | 可转换为bool | a.begin() == a.end() | 常量 |
- 那些标记了“(说明A)”或“(说明B)”的条目对于
array有线性复杂度,对于所有其它标准容器有常量复杂度。【注:算法equal()在第28章中定义。——注结束】 - 5 成员函数
size()返回容器中的元素个数。元素个数被构造函数、插入和删除的规则所定义。 - 6
begin()返回一个指向容器中首元素的迭代器,end()返回一个容器的逾尾值的迭代器。如果容器为空,那么begin() == end()。 - 7 在下列表达式中:
i == j
i != j
i < j
i <= j
i >= j
i > j
i - j
- 当
i和j表示容器的iterator类型的对象,其中一个或两个都可以被指向同一个元素且在语义上没有变化的容器的const_iterator类型的对象替换。 - 8 除非另有说明,在本章节中定义的所有容器都使用分配器获得内存(见20.5.3.5)。【注:特别地,容器和迭代器不存储对分配元素的引用而是通过分配器的指针类型,即,作为
P类型对象或pointer_traits<P>::template rebind<未指定>,且P为allocator_traits<allocator_type>::pointer。——注结束】这些容器类型的复制构造函数通过调用属于被复制的容器的分配器的allocator_traits<allocator_type>::select_on_container_copy_construction获得一个分配器。移动构造函数通过属于被移动容器的分配器的移动构造获得一个分配器。此类分配器的移动构造不应通过异常退出。这些容器类型的所有其它构造函数带有一个const allocator_type&参数。【注:如果构造函数的调用使用可选的分配器参数的默认值,那么Allocator类型必须支持值初始化。——注结束】在每个容器对象的生命周期中或直到分配器被替换为止,此分配器的副本用于由这些构造函数和所有成员函数执行的任何内存分配和元素构造。分配器只能通过赋值或swap()来替换。只要在相应容器的实现中,allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value、allocator_traits<allocator_type>::propagate_on_container_move_assignment::value或allocator_traits<allocator_type>::propagate_on_container_swap::value为真,分配器替换通过复制赋值、移动赋值或分配器的交换来执行。在本章定义的所有容器类型中,成员get_allocator()返回一个用于构造容器的分配器的副本,或如果该分配器已被替换,则是最近替换的副本。 - 9 表达式
a.swap(b),对于除array外标准容器类型的容器a和b,应在不调用任何单个容器元素的移动、复制或交换操作的情况下,交换a和b的值。任何属于a和b的Compare、Pred或Hash类型的左值应是可交换的,应通过调用描述在20.5.3.2中的swap来被交换。如果allocator_traits<allocator_type>::propagate_on_container_swap::value为真,那么allocator_type类型的左值应是可交换的,且a和b的分配器也应通过调用描述在20.5.3.2中的swap来被交换。否则,分配器不应被交换,且行为是未定义的,除非a.get_allocator() == b.get_allocator()。每一个在交换前指向一个容器中元素的迭代器都应在交换后指向另一个容器中的同一元素。在交换前,值为a.end()的迭代器是否拥有交换后的值b.end()是不确定的。 - 10 如果容器的迭代器类型属于双向或随机访问迭代器类别(27.2),则称该容器为可逆的,并满足表84中的附加要求。
| 表达式 | 返回类型 | 断言/说明 前置/后置条件 | 复杂度 |
|---|---|---|---|
X::reverse_iterator | 值类型为T的迭代器类型 | reverse_iterator<iterator> | 编译时 |
X::const_reverse_iterator | 值类型为T的常量迭代器类型 | reverse_iterator<const_iterator> | 编译时 |
a.rbegin() | reverse_iterator;对常量a是const_reverse_iterator | reverse_iterator(end()) | 常量 |
a.rend() | reverse_iterator;对常量a是const_reverse_iterator | reverse_iterator(end()) | 常量 |
a.crbegin() | const_reverse_iterator | const_cast<X const&>(a).rbegin() | 常量 |
a.crend() | const_reverse_iterator | const_cast<X const&>(a).rend() | 常量 |
- 11 除非另有规定(见26.2.6.1、26.2.7.1、26.3.8.4和26.3.11.5),本节中定义的所有容器类型满足以下附加要求:
- (11.1) ——当插入单个元素时,如果异常被
insert()或emplace()函数抛出,此函数无作用。 - (11.2) ——如果异常被
push_back()、push_front()、emplace_back()或emplace_front()函数抛出,此函数无作用。 - (11.3) ——
erase()、clear()、pop_back()或pop_front()函数不抛出异常。 - (11.4) ——复制构造函数或返回的迭代器的赋值操作符不抛出异常。
- (11.5) ——
swap()函数不抛出异常。 - (11.6) ——
swap()函数不使指向被交换容器元素的引用、指针或迭代器失效。【注:因为end()迭代器不指向任何元素,所以它可能会失效。——注结束】 - 12 除非另有规定(明确地或通过其他函数定义一个函数),调用容器成员函数或将容器作为参数传递给库函数,不应使指向容器中对象的迭代器失效或改变容器中对象的值。
- 13 连续容器是支持随机访问迭代器且成员类型
iterator和const_iterator为连续迭代器的容器。 - 14 表85列出了为某些容器类型而不是其它类型提供的操作。提供所列出操作的容器应实现表85中描述的语义,除非另有说明。
| 表达式 | 返回类型 | 操作 语义 | 断言/说明 前置/后置条件 | 复杂度 |
|---|---|---|---|---|
a < b | 可转换为bool | lexicographical_compare(a.begin(),a.end(), b.begin(), b.end()) | 要求:<被T的值定义。<是一种全序关系。 | 线性 |
a > b | 可转换为bool | b < a | 线性 | |
a <= b | 可转换为bool | !(a > b) | 线性 | |
a >= b | 可转换为bool | !(a < b) | 线性 |
- 【注:算法
lexicographical_compare()在第28章中定义。——注结束】 - 15 除
array外,本章和24.3.2中定义的容器满足表86中描述的分配器可感容器的附加要求。 - 给定分配器类型
A,给定具有与T相同的value_type和与allocator_traits<A>::rebind_alloc<T>相同的分配器类型的容器类型X,给定A类型的左值m、T*类型的指针p、T(可能为const)类型的表达式v和T类型的右值rv,定义以下术语。如果X不是分配器可感的,下面的术语被定义就像A是allocator<T>一样——无需创建任何分配器对象,且allocator<T>的用户特化不被实例化: - (15.1) ——
T到X中是DefaultInsertable,意味着下列表达式是规范的:
allocator_traits<A>::construct(m, p)
- (15.2) ——如果
X的元素被此表达式的赋值初始化,它是默认插入的,
allocator_traits<A>::construct(m, p)
- 其中
p为元素分配在X中未初始化的存储地址。 - (15.3) ——
T到X中是MoveInsertable,意味着下列表达式是规范的:
allocator_traits<A>::construct(m, p, rv)
- 且它的赋值导致以下后置条件存在:在赋值前,
*p的值等于rv的值。【注:rv仍是一个有效对象。它的状态是不确定的。——注结束】 - (15.4) ——
T到X中是CopyInsertable,意味着除T是MoveInsertable到X中外,下列表达式是规范的:
allocator_traits<A>::construct(m, p, v)
- 且它的赋值导致以下后置条件存在:
v的值未改变且等于*p。 - (15.5)
T从args到X中是EmplaceInsertable,对于零个或更多参数的args,意味着下列表达式是规范的:
allocator_traits<A>::construct(m, p, args)
- (15.6)
T从X中是Erasable,意味着下列表达式是规范的:
allocator_traits<A>::destroy(m, p)
- 【注:容器调用
allocator_traits<A>::construct(m, p, args)来使用args构造在p位置的元素,其中m == get_allocator()。allocator中的默认construct将调用::new((void*)p) T(args),但特化的分配器可以选择一个不同的定义。——注结束】 - 16 在表86中,
X表示一个value_type为T且使用A类型分配器的分配器可感的容器类,u表示一个变量,a和b表示X类型的非常量左值,t表示X类型的一个左值或常量右值,rv表示X类型的一个非常量右值,且m是A类型的值。
| 表达式 | 返回类型 | 断言/说明 前置/后置条件 | 复杂度 |
|---|---|---|---|
allocator_type | A | 要求:allocator_type::value_type与X::value_type相同。 | 编译时 |
get_allocator() | A | 常量 | |
X()X u; | 要求:A是DefaultConstructible。后置条件: u.empty()返回true,u.get_allocator() == A() | 常量 | |
X(m)X u(m); | 后置条件:u.empty()返回true,u.get_allocator() == m | 常量 | |
X(t, m)X u(t, m); | 要求:T到X中是CopyInsertable。后置条件: u == t,u.get_allocator() == m | 线性 | |
X rvX u(rv); | 后置条件:在构造前,u应有与rv相同的元素;在构造前,u.get_allocator()的值应与rv.get_allocator()的值相同。 | 常量 | |
X(rv, m)X u(rv, m); | 要求:T到X中是MoveInsertable。后置条件:在构造前, u应与rv有相同的元素或其副本,u.get_allocator() == m | 若m == rv.get_allocator()则为常量,否则为线性 | |
a = t | X& | 要求:T到X中是CopyInsertable且CopyAssignable。后置条件: a == t | 线性 |
a = rv | X& | 要求:若allocator_traits<allocator_type>::propagate_on_container_move_assignment::value为false,T到X中是MoveInsertable且MoveAssignable。a的所有存在元素被移动赋值或销毁。后置条件:在赋值前, a应等于rv的值 | 线性 |
a.swap(b) | void | 交换a和b的内容 | 常量 |
- 17 某些容器的成员函数和推导影响行为取决于类型是否规定为输入迭代器或分配器。实现决定一个类型不能为输入迭代器的程度是不确定的,除非最小整数类型不符合输入迭代器的标准。同样地,一个实现决定一个类型不能是一个分配器的程度是不确定的,除非作为一个最小类型
A不符合分配器的条件,除非它满足以下两个条件: - (17.1) ——限定标识符
A::value_type是有效的并表示一个类型(17.8.2)。 - (17.2) ——当视为一个未赋值的表达式时,表达式
declval<A&>().allocate(size_t{})是合法的。
26.2.2 容器数据竞争
- 1 为了避免数据竞争(20.5.5.9),实现应考虑以下函数为
const:begin、end、rbegin、rend、front、back、data、find、lower_bound、upper_bound、equal_range、at,及除了关联或无序关联容器之外的operator[]。 - 2 虽然20.5.5.9,但是当同一容器中不同元素中包含的对象的内容同时修改时,实现需要避免数据竞争,
vector<bool>除外。 - 3 【注:对于一个有大于一个大小的
vector<int> x,x[1] = 5和*x.begin() = 10可以在没有数据竞争的情况下同时执行,但是x[0] = 5和*x.begin() = 10同时执行可能会导致数据竞争。作为一般规则的例外,对于一个vector<bool> y,y[0] = true可以与y[1] = true竞争。——注结束】
26.2.3 序列容器
- 1 序列容器将一个有限集合的对象组织成严格的线性排列。库提供了四种基本的序列容器:
vector、forward_list、list和deque。此外,array被提供为一个提供有限序列操作的序列容器,因为它有固定数量的元素。该库还提供容器适配器,使构建抽象数据类型变得容易,例如stack和queue,通过使用基本顺序容器种类(或者通过使用用户可能定义的其他顺序容器)。 - 2 序列容器为程序员提供了不同的复杂性权衡,并应被相应地使用。
vector或array是应被默认使用的序列容器类型。当序列中部有频繁插入和删除时,list或forward_list应被使用。当大多数插入和删除发生在序列的开头或结尾时,deque是选择的数据结构。 - 3 在表87和88中,
X表示一个序列容器类;a表示一个包含元素类型T的类型X的一个值;u表示正被声明的变量的名称;如果限定标识符X::allocator_type是有效的且表示一个类型,A表示X::allocator_type,如果不,A表示allocator<T>;i和j
表示满足输入迭代器要求的迭代器且指向可隐式转换为value_type的元素;[i, j)表示一个有效的区间;il指定一个类型initializer_list<value_type>的对象;n表示类型X::size_type的一个值;p表示a的一个有效常量迭代器;q表示a的一个有效可解引用常量迭代器;[q1, q2)表示a中常量迭代器的一个有效区间;t表示X::value_type的一个左值或常量右值;rv表示X::value_type的一个非常量右值。Args表示一个模板参数包;args表示一个有Args&&模式的函数参数包。 - 4 表达式的复杂度是与序列相关的。
| 表达式 | 返回类型 | 断言/说明 前置/后置条件 |
|---|---|---|
X(n, t)X u(n, t); | 要求:T到X中应CopyInsertable。后置条件: distance(begin(), end()) == n用 t的n份副本构造一个序列容器 | |
X(i, j)X u(i, j); | 要求:T从*i到X中应EmplaceConstructible。对于vector,如果迭代器不满足前向迭代器要求(27.2.5), T到X中也应MoveInsertable。在区间 [i, j)中的每个迭代器应恰好被解引用一次。后置条件: distance(begin(), end()) == distance(i, j)构造一个与区间 [i, j)相等的序列容器 | |
X(il) | 相当于X(il.begin(), il.end()) | |
a = il | X& | 要求:T到X中CopyInsertable且CopyAssignable。将区间[il.begin(),il.end())赋值给a。a的所有存在元素被赋值或销毁。返回: *this。 |
a.emplace(p, args) | iterator | 要求:T从args到X中EmplaceConstructible。对于vector和deque,T到X中也MoveInsertable且MoveAssignable。效果:在 p前插入一个用std::forward<Args>(args)...构造的T类型对象。 |
a.insert(p, t) | iterator | 要求:T到X中应CopyInsertable。对于vector和deque,T也应CopyAssignable。效果:在 p前插入一份t的副本。 |
a.insert(p, rv) | iterator | 要求:T到X中应MoveInsertable。对于vector和deque,T也应MoveAssignable。效果:在 p前插入一份rv的副本。 |
a.insert(p, n, t) | iterator | 要求:T到X中应CopyInsertable且CopyAssignable。在 p前插入t的n份副本 |
a.insert(p, i, j) | iterator | 要求:T从*i到X中应EmplaceConstructible。对于vector和deque,T到X中也应MoveConstructible、MoveAssignable且可交换(20.5.3.2)。在区间 [i, j)中的每个迭代器应恰好被解引用一次。要求: i和j不是a中的迭代器。在 p前插入[i, j)中元素的副本 |
a.insert(p, il) | iterator | a.insert(p, il.begin(), il.end())。 |
a.erase(q) | iterator | 要求:对于vector和deque,T应MoveAssignable。效果:删除 q指向的元素。 |
a.erase(q1, q2) | iterator | 要求:对于vector和deque,T应MoveAssignable。效果:删除区间 [q1, q2)中的元素。 |
a.clear() | void | 销毁a中的所有元素。使指向a的元素的所有引用、指针和迭代器失效,且可能使逾尾迭代器失效。 后置条件: a.empty()返回true。复杂度:线性。 |
a.assign(i, j) | void | 要求:T从*i到X中应EmplaceConstructible。对于vector,如果迭代器不满足前向迭代器要求(27.2.5), T到X中也应MoveInsertable。在区间 [i, j)中的每个迭代器应恰好被解引用一次。要求: i和j不是a中的迭代器。用 [i, j)的一份副本替换a中元素。使指向a的元素的所有引用、指针和迭代器失效。对于 vector和deque,也使逾尾迭代器失效。 |
a.assign(il) | void | a.assign(il.begin(), il.end())。 |
a.assign(n, t) | void | 要求:T到X中应CopyInsertable且CopyAssignable。要求: T不是a中的引用。用 t的n份副本替换a中元素。对于vector和deque,也使逾尾迭代器失效。 |
- 5
a.insert(p, t)返回的迭代器指向插入到a中的t的副本。 - 6
a.insert(p, rv)返回的迭代器指向插入到a中的rv的副本。 - 7
a.insert(p, n, t)返回的迭代器指向插入到a中的第一个元素的副本;或如果n == 0,则为p。 - 8
a.insert(p, i, j)返回的迭代器指向插入到a中的第一个元素的副本;或如果i == j,则为p。 - 9
a.insert(p, il)返回的迭代器指向插入到a中的第一个元素的副本;或如果il为空,则为p。 - 10
a.emplace(p, args)返回的迭代器指向从args到a中构造的新元素。 - 11
a.erase(q)返回的迭代器指向在元素被删除之前紧跟在q后面的元素。如果不存在这样的元素,则返回a.end()。 - 12
a.erase(q1, q2)返回的迭代器指向在任何元素被删除之前q2指向的元素。如果不存在这样的元素,则返回a.end()。 - 13 对于每一个在本章和第24章中定义的序列容器:
-
- (13.1) 如果构造函数
template <class InputIterator>
X(InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
-
- 被用一个不符合输入迭代器的要求的
InputIterator类型调用,那么构造函数将不参与重载解决方案。
- 被用一个不符合输入迭代器的要求的
-
- (13.2) 如果有下列形式的成员函数:
template<class InputIterator>
返回类型 F(const_iterator p,
InputIterator first, InputIterator last); // 例如insert
template <class InputIterator>
返回类型 F(InputIterator first, InputIterator last); // 例如append、assign
template <class InputIterator>
返回类型 F(const_iterator i1, const_iterator i2,
InputIterator first, InputIterator last); // 例如replace
-
- 被用一个不符合输入迭代器的要求的
InputIterator类型调用,那么这些函数将不参与重载解决方案。
- 被用一个不符合输入迭代器的要求的
-
- (13.3) 序列容器的推导器将不参与重载解决方案,如果它有一个
InputIterator模板参数和为参数推导出的一个不符合输入迭代器的要求的类型,或它有一个Allocator模板参数和为参数推导出的一个不符合分配器的要求的类型。
- (13.3) 序列容器的推导器将不参与重载解决方案,如果它有一个
- 14 表88列出了为某些类型的序列容器而不是其他类型提供的操作。实现应为“容器”列中所示的所有容器类型提供这些操作,并应以均摊的常量时间执行这些操作。
| 表达式 | 返回类型 | 操作语义 | 容器 |
|---|---|---|---|
a.front() | reference;对常量a是const_reference | *a.begin() | basic_string、array、deque、forward_list、list、vector |
a.back() | reference;对常量a是const_reference | { auto tmp = a.end();--tmp;return *tmp; } | basic_string、array、deque、list、vector |
a.emplace_front(args) | reference | 在前端加入用std::forward<Args>(args)...构造的T类型对象。要求: T从args到X中应EmplaceConstructible。返回: a.front()。 | deque、forward_list、list |
a.emplace_back(args) | reference | 在后端加入用std::forward<Args>(args)...构造的T类型对象。要求: T从args到X中应EmplaceConstructible。对于vector,T到X中也应MoveInsertable。返回: a.back()。 | deque、list、vector |
a.push_front(t) | void | 在前端加入t的一份副本。要求: T到X中应CopyInsertable。 | deque、forward_list、list |
a.push_front(rv) | void | 在前端加入rv的一份副本。要求: T<到X中应MoveInsertable。 | deque、forward_list、list |
a.push_back(t) | void | 在后端加入t的一份副本。要求: T到X中应CopyInsertable。 | basic_string、deque、list、vector |
a.push_back(rv) | void | 在后端加入rv的一份副本。要求: T到X中应MoveInsertable。 | basic_string、deque、list、vector |
a.pop_front() | void | 销毁首元素。 要求: a.empty()应为false。 | deque、forward_list、list |
a.pop_back() | void | 销毁末元素。 要求: a.empty()应为false。 | basic_string、deque、list、vector |
a[n] | reference;对常量a是const_reference | *(a.begin() + n) | basic_string、array、deque、vector |
a.at(n) | reference;对常量a是const_reference | *(a.begin() + n) | basic_string、array、deque、vecto |
- 15 成员函数
at()提供对容器元素的边界检查访问。若n >= a.size(),at()抛出out_of_range。
26.2.4 节点句柄
26.2.4.1 node_handle概览
- 1 一个节点句柄是一个接受来自关联容器(26.2.6)或无序关联容器(26.2.7)的单个元素的所有权的对象。它可以用来将所有权转移到具有兼容节点的另一个容器中。具有兼容节点的容器具有相同的节点句柄类型。元素可以在表89的同一行中的容器类型之间在任一方向上传送。
- 2 如果节点句柄不是空的,那么它包含一个分配器,该分配器等于提取元素时容器的分配器。如果节点句柄为空,则不包含分配器。
map<K, T, C1, A> | map<K, T, C2, A> |
|---|---|
map<K, T, C1, A> | multimap<K, T, C2, A> |
set<K, C1, A> | set<K, C2, A> |
set<K, C1, A> | multiset<K, C2, A> |
unordered_map<K, T, H1, E1, A> | unordered_map<K, T, H2, E2, A> |
unordered_map<K, T, H1, E1, A> | unordered_multimap<K, T, H2, E2, A> |
unordered_set<K, H1, E1, A> | unordered_set<K, H2, E2, A> |
unordered_set<K, H1, E1, A> | unordered_multiset<K, H2, E2, A> |
- 3
node_handle句柄仅用于说明。允许实现提供等效的功能,而不提供具有此名称的类。 - 4 如果一个
pair的用户定义特化存在于pair<const Key, T>或pair<Key, T>,其中Key是容器的key_type,T是容器的mapped_type,则涉及节点句柄的操作行为是未定义的。
template<未指定>
class node_handle {
public:
// 这些类型声明在表90和表91中描述。
using value_type = 见下; // 映射容器不存在
using key_type = 见下; // 集合容器不存在
using mapped_type = 见下; // 集合容器不存在
using allocator_type = 见下;
private:
using container_node_type = 未指定;
using ator_traits = allocator_traits<allocator_type>;
typename ator_traits::rebind_traits<container_node_type>::pointer ptr_;
optional<allocator_type> alloc_;
public:
constexpr node_handle() noexcept : ptr_(), alloc_() {}
~node_handle();
node_handle(node_handle&&) noexcept;
node_handle& operator=(node_handle&&);
value_type& value() const; // 映射容器不存在
key_type& key() const; // 集合容器不存在
mapped_type& mapped() const; // 集合容器不存在
allocator_type get_allocator() const;
explicit operator bool() const noexcept;
bool empty() const noexcept;
void swap(node_handle&)
noexcept(ator_traits::propagate_on_container_swap::value ||
ator_traits::is_always_equal::value);
friend void swap(node_handle& x, node_handle& y) noexcept(noexcept(x.swap(y))) {
x.swap(y);
}
};
26.2.4.2 node_handle构造函数、复制和赋值
node_handle(node_handle&& nh) noexcept;
- 1 效果:构造一个使用
nh.ptr_初始化ptr_的node_handle。使用nh.alloc_移动构造alloc_。赋值nh.ptr_为nullptr,赋值nh.alloc_为nullopt。
node_handle& operator=(node_handle&& nh);
- 2 要求:
!alloc_或ator_traits::propagate_on_container_move_assignment为true,或alloc_ == nh.alloc_。 - 3 效果:
-
- (3.1) ——如果
ptr_ != nullptr,通过调用ator_traits::destroy销毁ptr_指向的container_node_type对象中的value_type子对象,然后通过调用ator_traits::rebind_traits<container_node_type>::deallocate释放ptr_。
- (3.1) ——如果
-
- (3.2) ——赋值
ptr_为nh.ptr_。如果!alloc_或ator_traits::propagate_on_container_move_assignment为true,移动赋值alloc_为nh.alloc_。
- (3.2) ——赋值
-
- (3.3) ——赋值
nh.ptr_为nullptr,赋值nh.alloc_为nullopt。
- (3.3) ——赋值
- 4 返回:
*this。 - 5 抛出:无。
26.2.4.3 node_handle析构函数
~node_handle();
- 1 效果:如果
ptr_ != nullptr,通过调用ator_traits::destroy销毁ptr_指向的container_node_type对象中的value_type子对象,然后通过调用ator_traits::rebind_traits<container_node_type>::deallocate释放ptr_。
26.2.4.4 node_handle观察器
value_type& value() const;
- 1 要求:
empty() == false。 - 2 返回:
ptr_指向的container_node_type对象中的value_type子对象的一个引用。 - 3 抛出:无。
key_type& key() const;
- 4 要求:
empty() == false。 - 5 返回:
ptr_指向的container_node_type对象中的value_type子对象的key_type成员的一个非常量引用。 - 6 抛出:无。
- 7 备注:允许通过返回的引用修改键。
mapped_type& mapped() const;
- 8 要求:
empty() == false。 - 9 返回:
ptr_指向的container_node_type对象中的value_type子对象的mapped_type成员的一个引用。 - 10 抛出:无。
allocator_type get_allocator() const;
- 11 要求:
empty() == false。 - 12 返回:
*alloc_。 - 13 抛出:无。
explicit operator bool() const noexcept;
- 14 返回:
ptr_ != nullptr。
bool empty() const noexcept;
- 15 返回:
ptr_ == nullptr。
26.2.4.5 node_handle修改器
void swap(node_handle& nh)
noexcept(ator_traits::propagate_on_container_swap::value ||
ator_traits::is_always_equal::value);
- 1 要求:
!alloc_,或!nh.alloc_,或ator_traits::propagate_on_container_swap为true,或alloc_ == nh.alloc_。 - 2 效果:调用
swap(ptr_, nh.ptr_)。如果!alloc_,或!nh.alloc_,或ator_traits::propagate_on_container_swap为true,调用swap(alloc_, nh.alloc_)。
26.2.5 插入返回类型
- 1 具有唯一键的关联容器和具有唯一键的无序容器具有一个成员函数
insert,它返回嵌套类型insert_return_type。此返回类型是本节中指定的类型的特化。
template <class Iterator, class NodeType>
struct INSERT_RETURN_TYPE
{
Iterator position;
bool inserted;
NodeType node;
};
- 2 名称
INSERT_RETURN_TYPE仅用作说明。INSERT_RETURN_TYPE具有上面指定的模板参数、数据成员和特殊成员。它没有指定的基类或成员。
26.2.6 关联容器
- 1 关联容器提供基于键的数据快速检索。库中提供了四种基本的关联容器:
set、multiset、map和multimap。 - 2 每个关联容器在
Key和在Key的元素上引起一个严格弱序(28.7)的排序关系Compare上被参数化。此外,map和multimap用Key关联了一个任意映射类型T。Compare类型的对象称为容器的比较对象。 - 3 “等价键”一词指的是比较所带来的等价关系,不是键上的
operator==。也就是说,k1和k2两个键,如果对于比较对象comp,comp(k1, k2) == false && comp(k2, k1) == false,则考虑为等价的。对于同一容器中的k1和k2两个键,调用comp(k1, k2)总应返回相同的值。 - 4 如果每个键最多包含一个元素,关联容器支持唯一键。否则,它支持等价键。
set和map类支持唯一键;multiset和multimap类支持等价键。对于multiset和multimap,insert、emplace和erase保持等价元素的相对顺序。 - 5 对于
set和multiset,值类型与键类型相同。对于map和multimap,它等于pair<const Key, T>。 - 6 一个关联容器的
iterator属于双向迭代器类别。对于值类型与键类型相同的关联容器,iterator和const_iterator都是常量迭代器。iterator和const_iterator是否是相同类型是不明确的。【注:在这种情况下,iterator和const_iterator具有相同的语义,且迭代器可转换为常量迭代器。用户可以通过在函数参数列表中总使用const_iterator来避免违反一个定义原则。——注结束】 - 7 关联容器满足分配器可感容器(26.2.1)的所有要求,除了对于
map和multimap在表83中对于value_type的要求以key_type和value_type替代。【注:例如,在某些情况中key_type和mapped_type要求CopyAssignable,即使相关的value_type、pair<const key_type, mapped_type>并非CopyAssignable。——注结束】 - 8 在表90中,
X表示一个关联容器类;a表示一个X类型的值;a2表示具有与X类型兼容的节点的类型的值(表89);b表示一个X类型的可能为const的值;u表示正被声明的变量的名称;当X支持唯一键时,a_uniq表示一个X类型的值;当X支持多重键时,a_eq表示一个X类型的值;当限定标识符X::key_compare::is_transparent有效且表示一个类型(17.8.2)时,a_tran表示一个X类型的可能为const的值;i和j满足输入迭代器要求且指向可隐式转换为value_type的元素;[i, j)表示一个合法区间;p表示a的一个合法常量迭代器;q表示a的一个可解引用的合法常量迭代器;r表示a的一个可解引用的合法迭代器;[q1, q2)表示a中常量迭代器的一个合法区间;il指定一个initializer_list<value_type>类型的对象;t表示类型X::value_type的一个值;k表示类型X::key_type的一个值,c表示X::key_compare类型的一个可能为const的值;kl是一个关于c(r, kl)使a被划分(28.7)的值,其中r是e的键值且e在a中;ku是一个关于!c(ku, r)使a被划分(28.7)的值;ke是一个关于c(r, ke)和!c(ke, r)使a被划分(28.7)的值,其中c(r, ke)暗指!c(ke, r)。如果有,A表示X使用的存储分配器,否则为allocator<X::value_type>;m表示一个可转换为A的类型的分配器;nh表示X::node_type类型的一个非常量右值。
| 表达式 | 返回类型 | 断言/说明 前置/后置条件 | 复杂度 |
|---|---|---|---|
X::key_type | Key | 编译时 | |
X::mapped_type(只有map和multimap) | T | 编译时 | |
X::value_type(只有set和multiset) | Key | 要求:value_type从X中是Erasable | 编译时 |
X::value_type(只有map和multimap) | pair<const Key, T> | 要求:value_type从X中是Erasable | 编译时 |
X::key_compare | Compare | 要求:key_compare是CopyConstructible | 编译时 |
X::value_compare | 二元谓词类型 | 对于set和multiset,与key_compare相同;对于map和multimap,是由一对中第一分量(即Key)引起的顺序关系 | 编译时 |
X::node_type | node_handle类模板的特化,使得公共嵌套类型与X中的对应类型相同。 | 见26.2.4 | 编译时 |
X(c)X u(c); | 效果:构造一个空容器。使用c的副本作为比较对象。 | 常量 | |
X()X u; | 要求:key_compare是DefaultConstructible。效果:构造一个空容器。使用 Compare()作为比较对象。 | 常量 | |
X(i,j,c)X u(i,j,c); | 要求:value_type从*i到X中是EmplaceConstructible。效果:构造一个空容器,向其中插入范围 [i, j)中的元素;使用c作为比较对象。 | 通常为N log N,其中N有值distance(i, j);若[i, j)被用value_comp()排序则为线性 | |
X(i,j)X u(i,j); | 要求:key_compare是DefaultConstructible。value_type从*i到X中是EmplaceConstructible。效果:同上,但使用 Compare()作为比较对象 | 同上 | |
X(il) | 和X(il.begin(), il.end())相同 | 和X(il.begin(), il.end())相同 | |
X(il,c) | 和X(il.begin(), il.end(), c)相同 | 和X(il.begin(), il.end(), c)相同 | |
a = il | X& | 要求:value_type到X中是CopyInsertable且CopyAssignable。效果:赋值区间 [il.begin(), il.end())到a中。a的所有存在元素被赋值或销毁。 | 通常为N log N,其中N有值il.size() + a.size();若[il.begin(), il.end())被用value_comp()排序则为线性 |
b.key_comp() | X::key_compare | 返回构造b的比较对象。 | 常量 |
b.value_comp() | X::value_compare | 返回由比较对象构造的value_compare对象 | 常量 |
a_uniq.emplace(args) | pair<iterator, bool> | 要求:value_type从args到a中应是EmplaceConstructible。效果:插入由 std::forward<Args>(args)...构造的value_type对象t,如果且仅如果容器中没有元素有和t键等价的键。返回的双对的bool成员是true,如果且仅如果本次插入发生;且双对的iterator成员指向有和t键等价的键的元素。 | 对数 |
a_eq.emplace(args) | iterator | 要求:value_type从args到a中应是EmplaceConstructible。效果:插入由 std::forward<Args>(args)...构造的value_type对象t,返回指向新插入元素的迭代器。如果一个区间包含和t等价的元素存在于a_eq中,t被插入到那个区间的末尾。 | 对数 |
@TODO: 849@

本文详细介绍了C++标准库中的容器组件,包括容器的基本概念、序列容器、关联容器等,并阐述了各种容器的特点和应用场景。
1166

被折叠的 条评论
为什么被折叠?



