跳过正文
  1. C++ 学习/

C++ for_each 遍历算法

·981 字·2 分钟· loading · loading · ·
Fairchild
作者
Fairchild
土木逆子
14:30
目录
cpp - 这篇文章属于一个选集。
§ 2: 本文

for_each() 是一个遍历算法,它对指定范围内的每个元素执行给定的可调用对象(仿函数、Lambda 表达式、函数指针),并返回该可调用对象,以便获取其最终状态。

#include <algorithm>
for_each(InputIterator first, InputIterator last, Function fn);

1 普通函数
#

std::for_each 传入普通函数时,函数名会自动转换为指向该函数的指针。

#include <iostream>
#include <vector>
#include <algorithm>

void print(int n) {
    std::cout << n << " ";
}

void doubleValue(int &n) {
    n *= 2;
}

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    std::for_each(vec.begin(), vec.end(), doubleValue);
    std::for_each(vec.begin(), vec.end(), print);
    return 0;
}

2 仿函数
#

仿函数(函数对象) 是重载了函数调用操作符 () 的类对象,它们可以像普通函数一样被调用,但对象内部存储的数据成员使其具有状态。

#include <iostream>
#include <vector>
#include <algorithm>

class Sum {
private:
    int total = 0;
public:
    void operator()(int n) {
        total += n;
    }
    int getTotal() const { return total; }
};

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    Sum sum = std::for_each(vec.begin(), vec.end(), Sum());
    std::cout << sum.getTotal() << std::endl;
    return 0;
}

以上 Sum() 是临时对象,for_each() 返回的是修改后的函数对象

// 错误写法
Sum s;
std::for_each(vec.begin(), vec.end(), s);           // 传入的是 s 的拷贝
// s.total 还是 0,因为修改的是拷贝

// 正确写法
Sum s;
std::for_each(vec.begin(), vec.end(), std::ref(s)); // 传引用

3 Lambda 表达式
#

Lambda 表达式是 C++11 引入的一种创建匿名函数对象的简洁语法,它允许在函数内部直接定义可调用代码块,并能通过捕获列表访问其作用域内的变量。

[captures] (params) lambda-specifiers -> return type { body }

captures 捕获列表,可以把上下文变量以值或引用的方式捕获,在 body 中使用
params 形参列表,可以省略,表示无参函数
lambda-specifiers 说明符,可选项有 mutable exception
return type 返回值类型,可以省略,由编译器自动推导
body 函数体,函数实现,可以为空,但是不能省略 {}

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    
    // 1. 统计偶数个数(按引用捕获外部作用域中所有变量)  
    int evenCount = 0;
    std::for_each(vec.begin(), vec.end(), 
        [&](int n) { if (n % 2 == 0) evenCount++; }
    );
    std::cout << "evenCount: " << evenCount << std::endl;
    
    // 2. 修改容器元素(用引用)
    std::for_each(vec.begin(), vec.end(), 
        [](int &n) { n *= 5; } 
    );

    // 3. 打印每个元素
    std::for_each(vec.begin(), vec.end(), 
        [](int n) { std::cout << n << " "; }
    );
    std::cout << std::endl;
    
    return 0;
}

4 函数指针
#

函数指针是指向函数的指针变量,它可以存储函数的地址,并通过该指针调用函数。

// 返回值类型 (*函数指针名)(参数列表)

#include <iostream>
#include <vector>
#include <algorithm>

int print(int& n) {
    std::cout << n << " ";
    return 0;
}

int square(int& n) {
    return n *= n;
}

class Line {
private:
    int cnt = 0;
public:
    void operator()() {
        cnt++;
        std::cout << std::endl << std::endl;
        std::cout << "------ " << cnt << " ------" << std::endl;
    }
};

int main() {
    Line line;
    std::vector<int> vec = {1, 2, 3, 4, 5};
    
    // 1. 直接使用函数名(函数名自动转换为指针)
    line();
    std::for_each(vec.begin(), vec.end(), print);
    
    // 2. 声明函数指针变量(指向接受一个 int&、返回 int 的函数)
    line();
    int (*printPtr)(int&) = print;
    std::for_each(vec.begin(), vec.end(), printPtr);
    
    // 3. 定义一个函数,接受函数指针作为参数
    line();
    auto processVector = [](std::vector<int>& v, int (*func)(int&)) {
        std::for_each(v.begin(), v.end(), func);
    };
    processVector(vec, square);
    processVector(vec, printPtr);
    
    // 4. 函数指针数组
    line();
    int (*operations[])(int&) = {square, print};
    for (auto op : operations) {
        std::for_each(vec.begin(), vec.end(), op);
    }
    
    // 5. 函数指针与 transform 结合
    line();
    int (*squarePtr)(int&) = square;
    std::vector<int> result(vec.size());
    std::transform(vec.begin(), vec.end(), result.begin(), squarePtr);
    std::for_each(result.begin(), result.end(), print);
    
    // 6. 使用 typedef 简化
    line();
    typedef int (*PrintFunc)(int&);
    PrintFunc pf = print;
    std::for_each(vec.begin(), vec.end(), pf);
    
    // 7. 使用 using 简化
    line();
    using UnaryOp = int(*)(int&);
    UnaryOp uop = print;
    std::for_each(vec.begin(), vec.end(), uop);
    
    return 0;
}

 Linux 下的 C++ 编程