第9.2章: 通用操作
概述
所有序列容器共享一大套通用操作。本节用完整的表格说明每项操作做什么、怎么用,以及哪些容器支持它。✓ 表示支持;— 表示不可用;加了注释则表示该操作存在,但语义有所不同。
1. 类型别名
每个容器都提供一组标准的嵌套类型别名。有了它们,你就能写出随容器的元素类型、大小类型和迭代器类别自动调整的泛型代码。
| 类型别名 | 说明 | 示例 | vector | deque | list | forward_list | array | bitvec | span | string_view |
|---|---|---|---|---|---|---|---|---|---|---|
value_type |
容器中存放的元素类型。 | ::std::vector<T>::value_type x; |
✓ | ✓ | ✓ | ✓ | ✓ | bool | ✓ | char_type |
size_type |
用于大小和下标的无符号整数类型。始终是 ::std::size_t。 |
using Sz = C::size_type; |
size_t | size_t | size_t | size_t | size_t | size_t | size_t | size_t |
difference_type |
用于迭代器差值的带符号整数类型。始终是 ::std::ptrdiff_t。 |
auto d = it2 - it1; |
ptrdiff_t | ptrdiff_t |
ptrdiff_t | ptrdiff_t |
ptrdiff_t | ptrdiff_t |
ptrdiff_t | ptrdiff_t |
reference |
解引用迭代器时返回的类型。对 bitvec 来说它是一个代理对象,而不是真正的引用。 |
T& r = v[0]; |
T& | T& | T& | T& | T& | — | T& | ch const& |
const_reference |
对元素的 const 限定引用。 | T const& r = v[0]; |
T const& | T const& | T const& | T const& | T const& | — | T const& | ch const& |
pointer |
指向 value_type 的指针。对 bitvec 来说它是代理迭代器,不是裸指针。 |
T* p = v.data(); |
T* | T* | T* | T* | T* | — | T* | ch const* |
| 迭代器类别 | 容器所支持的迭代器概念,它决定了能使用哪些算法。 | using ItCat = typename C::iterator_category; |
连续 | 随机访问 | 双向 | 向前 | 连续 | — | 连续 | 连续(const) |
reverse_iterator |
容器是否支持通过 rbegin() / rend() 进行反向遍历。 |
for (auto it = v.rbegin(); it != v.rend(); ++it) |
✓ | ✓ | ✓ | — | ✓ | — | ✓ | ✓ |
2. 容量操作
这些操作用于查询或调整容器在不重新分配的情况下能容纳的元素个数。
| 操作 | 说明 | 示例 | vector | deque | list | forward_list | array | bitvec | span | string_view |
|---|---|---|---|---|---|---|---|---|---|---|
empty() / is_empty() |
容器中没有元素时返回 true。 |
if (v.is_empty()) { ... } |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
size() |
返回当前存放的元素个数。 | auto n = v.size(); |
✓ | ✓ | — | —(见注释) | ✓(静态) | ✓ | ✓ | ✓ |
size_bytes() |
返回元素占用的总字节数(size() * sizeof(T))。 |
auto bytes = v.size_bytes(); |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
max_size() |
返回容器理论上最多能容纳的元素个数。 | auto m = v.max_size(); |
✓ | ✓ | ✓ | — | ✓ | ✓ | ✓ | ✓ |
capacity() |
返回在需要重新分配之前还能存放的元素个数。 | auto c = v.capacity(); |
✓ | ✓ | — | — | — | ✓ | — | — |
reserve() |
请求容量至少为 n 个元素,不改变 size()。 |
v.reserve(100zu); |
✓ | —(见 reserve_back) |
— | — | — | ✓ | — | — |
shrink_to_fit() |
请求释放未使用的容量以减少内存占用。可能使迭代器失效。 | v.shrink_to_fit(); |
✓ | ✓ | — | — | — | ✓ | — | — |
注意:list 和 forward_list 都不提供 size()。统计链表中的元素需要 Θ(n) 的遍历,因此刻意不提供 size,以免隐藏这笔代价。如果确实需要,请使用 ::std::ranges::distance()。
deque 说明:deque 不提供 reserve(),而是提供 reserve_back(n) 和 reserve_front(n),分别用于在两端独立预留容量。它还提供 front_capacity() 和 back_capacity()。
3. 元素访问
这些操作按下标或位置直接访问单个元素。
| 操作 | 说明 | 示例 | vector | deque | list | forward_list | array | span | string_view |
|---|---|---|---|---|---|---|---|---|---|
operator[] |
访问下标处的元素。带边界检查:越界时调用 fast_terminate()。 |
auto x = v[2zu]; |
✓ | ✓ | — | — | ✓ | ✓ | ✓ |
index_unchecked() |
访问下标处的元素,不做边界检查。更快,但越界时是未定义行为。 | auto x = v.index_unchecked(2zu); |
✓ | ✓ | — | — | ✓ | ✓ | ✓ |
front() |
返回第一个元素的引用。容器为空时是未定义行为。 | auto x = v.front(); |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
back() |
返回最后一个元素的引用。容器为空时是未定义行为。 | auto x = v.back(); |
✓ | ✓ | ✓ | — | ✓ | ✓ | ✓ |
data() |
返回指向底层连续存储的裸指针。 | T* p = v.data(); |
✓ | — | — | — | ✓ | ✓ | ✓ |
front_unchecked() |
返回第一个元素的引用,但不检查容器是否为空。 | auto x = v.front_unchecked(); |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
back_unchecked() |
返回最后一个元素的引用,但不检查容器是否为空。 | auto x = v.back_unchecked(); |
✓ | ✓ | ✓ | — | ✓ | ✓ | ✓ |
4. 迭代器操作
这些操作返回用于界定容器元素范围的迭代器。所有迭代器都遵循左闭约定:范围 [begin(), end()) 包含第一个元素,但不包含末尾之后的位置。
| 操作 | 说明 | 示例 | vector | deque | list | forward_list | array | span | string_view |
|---|---|---|---|---|---|---|---|---|---|
begin() / end() |
返回指向第一个元素的迭代器 / 指向最后一个元素之后位置的迭代器。 | for (auto it = v.begin(); it != v.end(); ++it) |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
cbegin() / cend() |
返回 const 迭代器。通过该迭代器无法修改元素。 | for (auto it = v.cbegin(); it != v.cend(); ++it) |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
rbegin() / rend() |
返回反向迭代器,从最后一个元素遍历到第一个元素。 | for (auto it = v.rbegin(); it != v.rend(); ++it) |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
before_begin() |
返回指向第一个元素之前位置的迭代器。只有 forward_list 提供它,用于在头部插入。 |
fl.insert_after(fl.before_begin(), val); |
— | — | — | ✓ | — | — | — |
5. 修改器
这些操作在容器中添加、删除或替换元素。
| 操作 | 说明 | 示例 | vector | deque | list | forward_list | bitvec |
|---|---|---|---|---|---|---|---|
clear() |
删除所有元素。内存不会被释放;容量保留下来以备复用。 | v.clear(); |
✓ | ✓ | ✓ | ✓ | ✓ |
clear_destroy() |
删除所有元素并释放全部内存。等价于先 clear() 再 shrink_to_fit()。 |
v.clear_destroy(); |
✓ | ✓ | ✓ | ✓ | ✓ |
push_back() |
在容器尾部添加 val 的副本。 |
v.push_back(42zu); |
✓ | ✓ | ✓ | — | ✓ |
push_front() |
在容器头部添加 val 的副本。 |
v.push_front(42zu); |
— | ✓ | ✓ | ✓ | — |
pop_back() |
删除最后一个元素。容器为空时是未定义行为。 | v.pop_back(); |
✓ | ✓ | ✓ | — | ✓ |
pop_front() |
删除第一个元素。容器为空时是未定义行为。 | v.pop_front(); |
— | ✓ | ✓ | ✓ | — |
emplace_back() |
在尾部就地构造元素,把 args... 转发给构造函数。 |
v.emplace_back(1zu, 2zu); |
✓ | ✓ | ✓ | — | — |
emplace_front() |
在头部就地构造元素,把 args... 转发给构造函数。 |
v.emplace_front(42zu); |
— | ✓ | ✓ | ✓ | — |
resize() |
改变元素个数。若在增长,新元素会被值初始化;若在收缩,末尾的元素会被删除。 | v.resize(10zu); |
✓ | ✓ | — | — | ✓ |
assign() |
替换整个内容。重载形式:assign(count, val) 或 assign(first, last)。 |
v.assign(5zu, 42zu); |
✓ | ✓ | — | — | — |
append_range() |
把范围 [first, last) 中的所有元素追加到容器尾部。 |
v.append_range(other.begin(), other.end()); |
✓ | ✓ | — | — | ✓ |
swap() |
交换两个同类型容器的内容。对大多数容器是 Θ(1)。 | v.swap(w); |
✓ | ✓ | ✓ | ✓ | ✓ |
要点
- 所有容器在大小、遍历与元素访问方面共享一套通用接口。
list和forward_list不提供size();forward_list还没有back()。list和forward_list不支持operator[],也不支持随机访问。- 只有
vector、deque、array以及连续视图提供data()。 clear()保留容量;clear_destroy()释放内存。deque是唯一同时具备push_front()和双端容量的容器。