1. for_each算法深度解析:从C++98到现代C++的演进之路
在C++标准库中,for_each算法堪称STL算法家族中的"常青树"。这个看似简单的遍历工具,从C++98时代一路走来,经历了auto、lambda和结构化绑定等现代特性的洗礼,其使用方式和应用场景也在不断演变。作为C++开发者,深入理解for_each的底层机制和现代用法,不仅能写出更优雅的代码,还能在适当的场景做出最优选择。
提示:for_each的核心价值在于它提供了一种与容器类型无关的统一遍历方式,这种抽象能力在现代C++多范式编程中尤为重要。
1.1 for_each的基本形态与工作原理
for_each的函数原型简洁明了:
cpp复制template<class InputIt, class UnaryFunction>
UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f);
这个模板函数接受三个参数:
first和last构成迭代器范围(左闭右开区间)f是一个一元函数对象,将对范围内的每个元素应用该函数
编译器在处理for_each时,实际上会生成类似如下的代码:
cpp复制while (first != last) {
f(*first);
++first;
}
这种实现方式带来了几个重要特性:
- 泛型设计:适用于任何满足输入迭代器要求的容器
- 无返回值:默认情况下不收集处理结果(C++11后可通过lambda捕获实现)
- 执行顺序:严格按迭代器顺序执行,适合需要保证顺序的场景
1.2 四种可调用对象形式详解
1.2.1 Lambda表达式(C++11+优选方案)
现代C++中最常用的形式,特别是在C++14引入泛型lambda后:
cpp复制std::vector<int> vec = {1, 2, 3};
std::for_each(vec.begin(), vec.end(), [](auto& x) {
x *= 2; // 修改元素
std::cout << x << " ";
});
Lambda的优势在于:
- 就地定义,代码紧凑
- 可捕获上下文变量(通过[=]或[&])
- 支持auto参数(C++14起)
1.2.2 函数指针(传统C风格)
适用于简单操作或需要复用的场景:
cpp复制void print(int x) { std::cout << x << " "; }
std::vector<int> vec = {1, 2, 3};
std::for_each(vec.begin(), vec.end(), print);
注意:函数指针形式无法捕获上下文状态,也不支持重载,在复杂场景中受限。
1.2.3 函数对象(仿函数)
提供有状态的函数对象:
cpp复制struct Accumulator {
int sum = 0;
void operator()(int x) { sum += x; }
};
std::vector<int> vec = {1, 2, 3};
Accumulator acc = std::for_each(vec.begin(), vec.end(), Accumulator());
std::cout << "Sum: " << acc.sum; //
