C++ 从入门到精通之五

大纲

C++ 容器

迭代器(Iterator)

迭代器的概念

C++ 迭代器(Iterator)是一种用于统一访问和遍历容器元素的抽象工具,可以理解为连接容器与算法之间的桥梁。迭代器通过类似指针的操作方式定位和访问容器中的元素,使 std::vector、std::list、std::map 等不同类型的容器都能够使用统一的遍历方式,同时屏蔽不同容器底层数据结构的差异。STL 算法通常并不直接依赖具体容器,而是通过迭代器对元素进行操作,从而实现数据结构与算法的分离,这也是 STL 泛型编程思想的重要体现。

提示

  • C++ 常用的数据结构,包括:栈、队列、链表、树、散列表(哈希表)、图等,推荐阅读书籍 《算法导论》。

迭代器的类型

五种迭代器的介绍

迭代器支持的操作

在 C++ 中,不同类型的迭代器所支持的操作是不一样的,如下图所示:

验证迭代器的类型
  • 验证迭代器的类型(一共有五种类型)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <array>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <set>
#include <typeinfo>
#include <vector>

// 重载函数,打印迭代器类型
void _display_category(std::random_access_iterator_tag tag) {
std::cout << "random_access_iterator_tag" << std::endl;
}

void _display_category(std::bidirectional_iterator_tag tag) {
std::cout << "bidirectional_iterator_tag" << std::endl;
}

void _display_category(std::forward_iterator_tag tag) {
std::cout << "forward_iterator_tag" << std::endl;
}

void _display_category(std::output_iterator_tag tag) {
std::cout << "output_iterator_tag" << std::endl;
}

void _display_category(std::input_iterator_tag tag) {
std::cout << "input_iterator_tag" << std::endl;
}

template <typename T>
void display_category(T ite) {
std::cout << "----------begin----------" << std::endl;
// 通过萃取获取 T 迭代器的类型
typename std::iterator_traits<T>::iterator_category cagy;
// 打印迭代器的类型
_display_category(cagy);
std::cout << "typeof(ite).name() = " << typeid(ite).name() << std::endl;
std::cout << "-----------end-----------" << std::endl;
}

int main() {
// std::array<int, 100>::iterator() 等写法是构造一个临时的迭代器对象,只为了把它的类型传给模板参数 T,并不真正遍历容器
display_category(std::array<int, 100>::iterator());
display_category(std::vector<int>::iterator());
display_category(std::list<int>::iterator());
display_category(std::map<int, int>::iterator());
display_category(std::set<int>::iterator());
return 0;
}

在 Windows 平台 VS 2026 中,程序运行输出的结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
----------begin----------
random_access_iterator_tag
typeof(ite).name() = class std::_Array_iterator<int,100>
-----------end-----------
----------begin----------
random_access_iterator_tag
typeof(ite).name() = class std::_Vector_iterator<class std::_Vector_val<struct std::_Simple_types<int> > >
-----------end-----------
----------begin----------
bidirectional_iterator_tag
typeof(ite).name() = class std::_List_iterator<class std::_List_val<struct std::_List_simple_types<int> > >
-----------end-----------
----------begin----------
bidirectional_iterator_tag
typeof(ite).name() = class std::_Tree_iterator<class std::_Tree_val<struct std::_Tree_simple_types<struct std::pair<int const ,int> > > >
-----------end-----------
----------begin----------
bidirectional_iterator_tag
typeof(ite).name() = class std::_Tree_const_iterator<class std::_Tree_val<struct std::_Tree_simple_types<int> > >
-----------end-----------

在 Linux 平台 CLion 中,程序运行输出的结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
----------begin----------
random_access_iterator_tag
typeof(ite).name() = Pi
-----------end-----------
----------begin----------
random_access_iterator_tag
typeof(ite).name() = N9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE
-----------end-----------
----------begin----------
bidirectional_iterator_tag
typeof(ite).name() = St14_List_iteratorIiE
-----------end-----------
----------begin----------
bidirectional_iterator_tag
typeof(ite).name() = St17_Rb_tree_iteratorISt4pairIKiiEE
-----------end-----------
----------begin----------
bidirectional_iterator_tag
typeof(ite).name() = St23_Rb_tree_const_iteratorIiE
-----------end-----------

迭代器的分类

在 C++ 中,常见的迭代器有以下几类:

  • iterator:
    • 可读写正向迭代器;
    • ++ 向尾部方向移动,*it 通常返回可修改引用;
    • 由 begin() / end() 获得。
  • const_iterator:
    • 只读正向迭代器;
    • ++ 向尾部方向移动,但 *it 返回 const 引用,不能通过它修改元素;
    • 由 cbegin() / cend() 获得,可由 iterator 转换而来。
  • reverse_iterator:
    • 可读写反向迭代器;通常是对可读写正向迭代器的适配器(如 reverse_iterator<iterator>);
    • ++ 向头部方向移动,*it 通常可修改;
    • 由 rbegin() / rend() 获得。
  • const_reverse_iterator:
    • 只读反向迭代器;通常是对只读正向迭代器的反向适配(如 reverse_iterator<const_iterator>);
    • ++ 向头部方向移动,*it 为 const,不能修改元素;
    • 由 crbegin() / crend() 获得。

提示

  • 在 C++ 中,iterator 迭代器是从 const_iterator 迭代器继承而来的。
  • 值得一提的是,并不是所有 STL 容器都有公开的迭代器接口,比如容器适配器 queue、stack、priority_queue 都不提供自己的迭代器,也不提供 begin() / end()。

迭代器的使用

iterator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <vector>

int main() {
std::vector<int> v = {1, 2, 3};

// 可读写正向迭代器
for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
// 可以更改元素值
*it = *it * 2;
std::cout << *it << std::endl;
}

return 0;
}

程序运行输出的结果如下:

1
2
3
2
4
6
const_iterator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <vector>

int main() {
std::vector<int> v = {1, 2, 3};

// 只读正向迭代器
for (std::vector<int>::const_iterator it = v.cbegin(); it != v.cend(); ++it) {
// 不可以更改元素值
std::cout << *it << std::endl;
}

return 0;
}

程序运行输出的结果如下:

1
2
3
1
2
3
reverse_iterator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <vector>

int main() {
std::vector<int> v = {1, 2, 3};

// 可读写反向迭代器
for (std::vector<int>::reverse_iterator it = v.rbegin(); it != v.rend(); ++it) {
// 可以更改元素值
*it = *it * 2;
std::cout << *it << std::endl;
}

return 0;
}

程序运行输出的结果如下:

1
2
3
6
4
2
const_reverse_iterator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <vector>

int main() {
std::vector<int> v = {1, 2, 3};

// 只读反向迭代器
for (std::vector<int>::const_reverse_iterator it = v.crbegin(); it != v.crend(); ++it) {
// 不可以更改元素值
std::cout << *it << std::endl;
}

return 0;
}

程序运行输出的结果如下:

1
2
3
3
2
1

算法(Algorithm)

算法的概述

C++ 标准库算法以迭代器为桥梁,覆盖查找、排序、修改、数值、集合、排列等常见需求,算法主要集中在 <algorithm>、<numeric>、<functional> 等头文件中,通过迭代器与容器解耦,因此同一套算法可以作用于 vector、list、deque、map、set、数组、甚至自定义容器。传统算法灵活,但写法偏底层;C++ 20 ranges 算法更现代、更安全、更易组合。实际开发中应根据容器类型、迭代器类别、性能需求和代码可读性选择合适算法,并注意逻辑删除、排序前提、比较器严格弱序等常见算法使用陷阱。

  • 算法的基本特点

    • 算法通过迭代器区间 [first, last) 操作元素,不直接依赖具体容器。
    • 大多数算法不改变容器大小,只重排、读取或修改已有元素。
    • 涉及插入 / 删除的算法(如 remove、unique)通常只做逻辑删除,真正删除需要配合容器的 erase。
    • 算法可分为非修改序列算法、修改序列算法、排序相关算法、查找算法、数值算法、集合算法等。
    • C++ 11 起大量算法增加了 _if、_copy、_n 等变体,以及 Lambda 表达式支持。
    • C++ 17 起引入并行执行策略(如 std::execution::par)。
    • C++ 20 起引入 ranges 版本(std::ranges::),可直接作用于容器,更简洁安全。
  • 算法的注意事项

    • 算法不检查区间越界,需保证迭代器有效。
    • 逻辑删除算法(remove、unique)必须配合容器的 erase 才能真正删除元素。
    • sort、stable_sort 等要求使用随机访问迭代器,list 应使用其成员函数 sort。
    • lower_bound 等二分算法要求区间已按相同规则排序。
    • 自定义比较函数必须满足严格弱序,否则行为未定义。
    • 使用 Lambda 表达式时,需要注意捕获方式,避免悬垂引用。
    • 若需并行执行,可使用 std::execution::par 等策略,但必须保证并行操作无数据竞争且满足并行算法的要求(如不依赖执行顺序)。

提示

  • C++ 算法可以简单理解为 std 命名空间中的全局函数或全局函数模板。
  • C++ 算法的前两个形参通常都是迭代器类型,用来表示容器中元素的一个区间,通常是左闭右开区间 [first, last)。

算法的实现

在 C++ 算法的内部实现中,算法会根据传入的迭代器分析出它的种类(五种之一);不同种类的迭代器,算法会有不同的处理方式,需要编写不同的代码来应对,而这种针对不同迭代器种类编写不同代码的做法,会直接影响算法的执行效率(效率是一个很重要的指标),这也正是 STL 内部为什么要给迭代器做分类的目的。

算法的分类

非修改序列算法

这类算法不会修改元素,只做查询、计数、遍历。

  • for_each:对区间内每个元素执行操作。
  • count / count_if:统计等于某值或满足条件的元素个数。
  • find / find_if / find_if_not:查找第一个匹配元素。
  • find_end / find_first_of:查找子序列或任意匹配元素。
  • adjacent_find:查找相邻重复元素。
  • search / search_n:查找子序列或连续 n 个匹配元素。
  • all_of / any_of / none_of:判断是否全部、任一或没有元素满足条件。
  • equal:比较两个区间是否相等。
  • mismatch:找出两个区间第一个不匹配的位置。
  • lexicographical_compare:按字典序比较两个区间。
  • is_sorted / is_sorted_until:判断区间是否已排序。
  • is_partitioned / partition_point:判断或定位划分点。
修改序列算法

这类算法会修改元素值或重新排列元素。

  • copy / copy_if / copy_n / copy_backward:复制区间。
  • move / move_backward:移动区间元素。
  • fill / fill_n:填充指定值。
  • transform:对每个元素做变换,可一元或二元。
  • generate / generate_n:用生成器函数填充区间。
  • remove / remove_if:逻辑删除,返回新逻辑结尾。
  • remove_copy / remove_copy_if:删除并复制到另一区间。
  • replace / replace_if:替换满足条件的元素。
  • replace_copy / replace_copy_if:替换并复制。
  • swap / swap_ranges / iter_swap:交换元素或区间。
  • reverse / reverse_copy:反转区间。
  • rotate / rotate_copy:旋转区间。
  • unique / unique_copy:去除相邻重复元素。
  • shuffle:随机打乱区间(C++ 11)。
  • sample:随机抽样(C++ 17)。
  • partition / stable_partition:按条件划分区间。
  • partition_copy:划分并复制到两个区间。
排序相关算法
  • sort:快速排序的混合实现,平均时间复杂度 O(N log N),不稳定。
  • stable_sort:稳定排序,保持相等元素相对顺序。
  • partial_sort / partial_sort_copy:部分排序,取前 k 个。
  • nth_element:将第 n 个元素放到最终位置,左边不大于它,右边不小于它。
  • is_sorted / is_sorted_until:判断排序状态。
  • merge / inplace_merge:合并两个有序区间。
  • make_heap / push_heap / pop_heap / sort_heap:堆操作。
  • is_heap / is_heap_until:判断是否为堆。
  • lower_bound / upper_bound / equal_range:有序区间二分查找。
  • binary_search:有序区间判断是否存在某值。
查找与搜索算法
  • find / find_if / find_if_not:线性查找。
  • find_end / find_first_of:子序列查找。
  • adjacent_find:相邻重复查找。
  • search / search_n:子序列或连续 n 个匹配。
  • lower_bound / upper_bound / equal_range / binary_search:有序区间二分查找。
  • max_element / min_element / minmax_element:最值查找。
  • clamp:将值限制在区间内(C++ 17)。
数值算法
  • accumulate:累加或自定义累积。
  • inner_product:内积。
  • adjacent_difference:相邻差分。
  • partial_sum:前缀和。
  • iota:填充递增序列。
  • reduce:并行友好归约(C++ 17)。
  • transform_reduce:变换后归约(C++ 17)。
  • exclusive_scan / inclusive_scan:前缀扫描(C++ 17)。
  • gcd / lcm:最大公约数、最小公倍数(C++ 17)。
  • midpoint:中点计算(C++ 20)。
集合算法

这类算法用于有序区间上的集合操作。

  • set_union:并集。
  • set_intersection:交集。
  • set_difference:差集。
  • set_symmetric_difference:对称差集。
  • includes:判断是否包含子集。
  • merge:合并两个有序区间。
排列与组合算法
  • next_permutation:下一个字典序排列。
  • prev_permutation:上一个字典序排列。
  • is_permutation:判断是否为同一排列。
Ranges 算法

C++ 20 新引入了 <ranges> 头文件与 std::ranges:: 命名空间,主要改进如下:

  • 可以直接传容器,如 std::ranges::sort(v);
  • 支持投影(Projection),如按成员排序。
  • 支持 views 与惰性求值,如 views::filter、views::transform。
  • 算法约束更强,错误信息更清晰。
  • 与管道 | 组合,代码更简洁。

常见 Ranges 算法与经典算法同名,如 ranges::find、ranges::sort、ranges::transform 等。

算法的使用

特别注意

当同名的成员函数和全局函数(算法)同时存在时,应优先考虑使用容器自带的同名成员函数,只有在容器没有提供同名成员函数的情况下,才考虑使用这些全局算法。这是因为成员函数通常更了解容器自身的内部结构,能够针对特定容器做出更高效的实现,而全局算法受限于通用迭代器接口,往往无法达到同等效率。

for_each
  • for_each:对区间内每个元素执行指定操作
1
2
3
4
5
6
7
8
9
10
11
12
#include <algorithm>
#include <iostream>
#include <vector>

int main() {
std::vector<int> numbers = {69, 23, 35, 13, 56};

// 第三个参数是可调用对象(如普通函数、函数对象、Lambda 表达式)
std::for_each(numbers.begin(), numbers.end(), [](const int& number) { std::cout << number << " "; });

return 0;
}

程序运行输出的结果如下:

1
69 23 35 13 56 
find
  • find:查找第一个匹配的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <algorithm>
#include <iostream>
#include <vector>

int main() {
const int target_number = 35;

std::vector<int> numbers = {69, 23, 35, 13, 56};

// 查找第一个值是 35 元素
std::vector<int>::iterator find_iter = std::find(numbers.begin(), numbers.end(), target_number);

if (find_iter != numbers.end()) {
std::cout << "found number " << *find_iter << std::endl;
} else {
std::cout << "not found number " << target_number << std::endl;
}

return 0;
}

程序运行输出的结果如下:

1
found number 35
find_if
  • find_if:根据条件查找第一个匹配的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <algorithm>
#include <iostream>
#include <vector>

int main() {
std::vector<int> numbers = {69, 23, 35, 13, 56};

// 查找第一个值大于 15 的元素,第三个参数是可调用对象(如普通函数、函数对象、Lambda 表达式)
std::vector<int>::iterator find_iter = std::find_if(numbers.begin(), numbers.end(), [](const int& number) { return number > 15; });

if (find_iter != numbers.end()) {
std::cout << "found number > 15, it is " << *find_iter << std::endl;
} else {
std::cout << "not found number > 15" << std::endl;
}

return 0;
}

程序运行输出的结果如下:

1
found number > 15, it is 69
sort
  • sort:快速排序的混合实现,平均时间复杂度 O(N log N),不稳定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <algorithm>
#include <iostream>
#include <vector>
#include <functional>

int main() {
std::vector<int> numbers = {69, 23, 35, 13, 56};

// 排序(默认升序排序,即从小到大排序)
std::sort(numbers.begin(), numbers.end());

// 或者降序排序(即从大到小排序),第三个参数是可调用对象(如普通函数、函数对象、Lambda 表达式)
// std::sort(numbers.begin(), numbers.end(), std::greater<int>());

for (const int& number : numbers) {
std::cout << number << " ";
}

return 0;
}

程序运行输出的结果如下:

1
13 23 35 56 69 

特别注意

  • std::sort() 要求迭代器是随机访问迭代器(Random Access Iterator),并且元素必须支持可比较的排序规则(默认 operator <,即从小到大排序)。
  • std::unordered_set 的迭代器是前向迭代器(Forward Iterator),不是随机访问迭代器,因此不能用 std::sort() 排序;如果需要对它的元素排序,应先拷贝到 std::vector 等支持随机访问的容器中再排序。
  • C++ 标准库中 std::set / std::map 是按红黑树有序存储的,但它们的迭代器是双向迭代器,同样不满足 std::sort() 的随机访问要求,所以也不能用 std::sort() 排序;但它们的底层本身已是按 operator < 有序存储的,不需要再排序。

函数对象(Function Object)

函数对象的概念

C++ 中的函数对象(又称仿函数)是指任何重载了函数调用运算符 operator() 的类或结构体实例,使其能够像普通函数一样以 对象(参数) 的形式被调用,同时还能携带状态(通过成员变量)并拥有自己的类型,从而可以配合模板、标准算法和 std::function 等机制实现灵活的回调、策略配置和延迟执行,相比普通函数指针更具表达力和内联优化潜力。

  • C++ 中的函数对象,其作用类似 C 语言中的函数指针。
  • 在 C++ 中,将拥有 operator() 小括号运算符重载函数的对象称作 “函数对象”,或者称作 “仿函数”。
  • 通过函数对象调用 operator(),会产生内联的效果,其执行效率比较高,因为没有函数调用的开销。
  • 由于函数对象是使用类生成的,因此函数对象可以拥有相关的成员变量,比如可以通过成员变量来记录函数对象的使用次数。
  • 在 C++ 中,常见的函数对象有 std::less、std::greater 等。

函数对象的使用

自定义函数对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <algorithm>
#include <iostream>
#include <vector>

// 函数对象
class CompareInt {
public:
// 重载 operator() 运算符
bool operator()(const int i, const int j) const {
// 升序排序(从小到大)
return i > j;
}
};

int main() {
std::vector<int> numbers = {69, 23, 35, 13, 56};

// 使用自定义的函数对象进行排序,CompareInt() 会生成一个临时对象(可调用对象)
std::sort(numbers.begin(), numbers.end(), CompareInt());

for_each(numbers.begin(), numbers.end(), [](const int& number) { std::cout << number << " "; });

return 0;
}

程序运行输出的结果如下:

1
69 56 35 23 13 
标准库中的函数对象

C++ 标准库中的函数对象都定义在 <functional> 头文件中,常见的函数对象如下图所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

int main() {
std::vector<int> numbers = {69, 23, 35, 13, 56};

// 使用 C++ 标准库中的函数对象进行降序排序(从大到小),std::greater<int>() 会生成一个临时对象(可调用对象)
std::sort(numbers.begin(), numbers.end(), std::greater<int>());

for_each(numbers.begin(), numbers.end(), [](const int& number) { std::cout << number << " "; });

return 0;
}

程序运行输出的结果如下:

1
69 56 35 23 13 

函数适配器(Function Adapter)

函数适配器的概述

C++ 函数适配器(在部分网络资料中也叫算法适配器)是标准库中一类用于包装、改造或组合可调用对象的工具,它们在不修改原有函数或函数对象的前提下,改变其调用方式或参数形式,从而让同一个算法能适配更多场景。早期的 bind1st、bind2nd、ptr_fun、mem_fun 等已基本被 C++ 11 起的 std::bind、std::function 和 Lambda 表达式取代;如今更常用的适配器包括 std::bind(绑定部分参数、调整参数顺序)、std::not_fn(对谓词结果取反)、std::mem_fn(把成员函数指针变成可调用对象)以及 std::reference_wrapper(配合 std::ref / std::cref 让算法按引用而非拷贝操作元素)。它们共同的核心思想是以可调用对象为输入、以新的可调用对象为输出,从而与 std::sort、std::find_if、std::transform 等算法灵活配合,提升代码的复用性和表达力。

函数适配器的使用

std::bind 使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

int main() {
std::vector<int> vec = {50, 16, 80, 25, 46, 80};

// 统计值小于 40 的元素个数
// std::bind 会生成一个新的可调用对象(函数对象):调用它时,传入的第 1 个实参传给 std::less<int> 的第 1 个参数,std::less<int> 的第 2 个参数固定绑定为 40,因此等价于判断 "元素 < 40"
const size_t less_total = std::count_if(vec.begin(), vec.end(), std::bind(std::less<int>(), std::placeholders::_1, 40));
std::cout << "less total: " << less_total << std::endl;

// 统计值大于 40 的元素个数
// std::bind 会生成一个新的可调用对象(函数对象):调用它时,传入的第 1 个实参传给 std::greater<int> 的第 1 个参数,std::greater<int> 的第 2 个参数固定绑定为 40,因此等价于判断 "元素 > 40"
const size_t greater_total = std::count_if(vec.begin(), vec.end(), std::bind(std::greater<int>(), std::placeholders::_1, 40));
std::cout << "greater total: " << greater_total << std::endl;

return 0;
}

程序运行输出的结果如下:

1
2
less total: 2
greater total: 4

迭代器适配器(Iterator Adapter)

迭代器适配器的概述

C++ 中的迭代器适配器是标准库适配器模式在迭代器层面的体现,其核心思想是:在不修改原有迭代器和容器接口的前提下,通过包装一个已有迭代器,产生一个行为不同、但对外仍表现为迭代器的新迭代器,从而让算法能够适配更多场景。按功能和用途,常见的迭代器适配器可以分为以下几类:

  • 反向迭代器:

    • 将迭代器的前进方向反过来,递增变成递减、递减变成递增,从而让算法可以反向遍历容器。
    • std::reverse_iterator,由容器的 rbegin() / rend() 返回,crbegin() / crend() 返回其 const 版本。
  • 插入迭代器:

    • 将对迭代器的 “赋值” 操作转换为 “插入” 操作,使 std::copy 等算法能够向容器中追加元素,而不是覆盖已有元素。
    • std::insert_iterator(由 std::inserter 创建,在指定位置之前插入)。
    • std::front_insert_iterator(由 std::front_inserter 创建,向容器头部插入)。
    • std::back_insert_iterator(由 std::back_inserter 创建,向容器尾部插入)。
  • 移动迭代器:

    • 解引用时返回右值引用而非左值引用,使算法在搬运元素时触发移动语义,避免不必要的拷贝。
    • std::move_iterator,由 std::make_move_iterator 创建,常用于把元素从一个容器 “搬” 到另一个容器。
  • 流迭代器:

    • 将输入流或输出流包装成迭代器,使标准算法可以直接从流中读取数据或向流中写入数据。
    • std::istream_iterator(从输入流读取)。
    • std::ostream_iterator(向输出流写入)。
    • std::wistream_iterator、std::wostream_iterator 等以及宽字符版本。
  • C++ 20 及之后新增的适配器:

    • std::common_iterator 用于把不同类型的迭代器与哨兵统一成同一类型。
    • std::counted_iterator 给迭代器附加计数、限定前进步数。
    • std::move_sentinel、std::basic_const_iterator 等迭代器由 C++ 23 新引入。

总结

总体来看,迭代器适配器(改造迭代器)与函数适配器(改造可调用对象,如 std::bind、std::not_fn、std::mem_fn)、容器适配器(改造容器接口,如 std::stack、std::queue、std::priority_queue)共同构成了 C++ 标准库中适配器模式的三种典型应用。它们共同的价值在于:以组合和包装的方式复用已有组件,在不改变原有类型的前提下扩展其行为,提升算法的通用性与代码的表达力。

迭代器适配器的使用

  • std::move_iterator:解引用时返回右值引用而非左值引用,使算法在搬运元素时触发移动语义,避免不必要的拷贝。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <iterator>
#include <string>
#include <vector>

int main() {
std::vector<std::string> src = {"apple", "banana", "cherry"};

// 使用 move_iterator 包装 std::vector 的迭代器,将 std::vector<std::string> 中的元素 "搬" 到另一个 vector,而不是逐个拷贝字符串
std::vector<std::string> dst(std::make_move_iterator(src.begin()), std::make_move_iterator(src.end()));

// 遍历 dst 中的所有元素
std::cout << "dst: ";
for (const auto& s : dst) {
std::cout << s << " ";
}
std::cout << std::endl;

// src 里的字符串内容被 "搬走" 后,通常变成空串(具体内容取决于编译器实现,标准库只保证 "有效但未指定")
std::cout << "src after move: ";
for (const auto& s : src) {
std::cout << "[" << s << "] ";
}
std::cout << std::endl;

return 0;
}

程序运行输出的结果如下:

1
2
dst: apple banana cherry 
src after move: [] [] []

代码分析

  • std::make_move_iterator(it) 把普通迭代器 it 包装成 std::move_iterator。
  • 解引用这个适配器时,返回的是右值引用(std::string&&),于是 std::vector 的区间构造函数会调用移动构造,而不是拷贝构造。
  • 常用于:把元素从一个容器搬到另一个容器、把容器元素搬进算法(如 std::copy、std::uninitialized_copy)等场景。
  • 特别注意:移动后源容器元素仍可安全析构和重新赋值,但不应再依赖其原值。