4. Lambda与STL算法:std::for_each、std::find_if、std::sort中的Lambda应用

说实话,刚学C++那会儿,我对STL算法是又爱又恨。爱的是它功能强大,恨的是每次都要写个函数对象,代码又长又绕。直到Lambda表达式来了,我才觉得STL算法真正「活」了过来。

今天我们就聊聊三个最常用的算法——std::for_eachstd::find_ifstd::sort。它们和Lambda搭配起来,写代码就像在写业务逻辑本身,而不是在跟编译器较劲。

4.1 std::for_each:遍历的优雅写法

std::for_each说白了就是「对容器里的每个元素做点什么事」。以前你得写个循环,现在一行Lambda搞定。

我个人的习惯是:如果只是简单打印或修改,用for_each;如果还要中途跳出,那就用for循环。你想想看,for_each没有break,这是它的局限。

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

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

    // 传统方式:写个函数对象
    struct Print {
        void operator()(int n) const {
            std::cout << n << " ";
        }
    };
    std::for_each(nums.begin(), nums.end(), Print());

    // Lambda方式:清爽多了
    std::for_each(nums.begin(), nums.end(),
        [](int n) { std::cout << n << " "; });

    // 带状态的Lambda:统计总和
    int sum = 0;
    std::for_each(nums.begin(), nums.end(),
        [&sum](int n) { sum += n; });
    std::cout << "\n总和: " << sum;

    return 0;
}

小技巧:如果你要修改容器元素,记得用引用捕获。比如 [&](int& n) { n *= 2; }。我刚开始就犯过这个错——传了值进去,改了半天原容器纹丝不动。

4.2 std::find_if:按条件查找

std::find_if是我项目里用得最多的算法之一。它返回第一个满足条件的元素的迭代器。找不到就返回end()

我记得有一次写一个用户管理系统,要从一堆用户里找出第一个VIP用户。用Lambda写起来就像在描述需求:「找那个等级大于5的」。

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

struct User {
    std::string name;
    int level;
};

int main() {
    std::vector<User> users = {
        {"Alice", 3},
        {"Bob", 7},
        {"Charlie", 5},
        {"David", 2}
    };

    // 查找第一个等级大于5的用户
    auto it = std::find_if(users.begin(), users.end(),
        [](const User& u) { return u.level > 5; });

    if (it != users.end()) {
        std::cout << "找到: " << it->name << "\n";
    } else {
        std::cout << "没找到\n";
    }

    // 带外部条件的查找
    int minLevel = 4;
    auto it2 = std::find_if(users.begin(), users.end(),
        [minLevel](const User& u) { return u.level >= minLevel; });

    return 0;
}

注意std::find_if返回的是迭代器,使用前一定要检查是否等于end()。我曾经在项目里忘了检查,直接解引用,程序就崩了。嗯,那是个周五下午,debug到晚上八点才找到原因。

4.3 std::sort:自定义排序

std::sort默认是升序排列。但实际项目中,排序规则千奇百怪——按分数降序、按名字长度、甚至按某个字段的哈希值。Lambda在这里简直是救星。

我个人觉得,std::sort的Lambda参数是最直观的:你只要告诉它「a应该在b前面吗?」返回true就表示a在前。

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

struct Student {
    std::string name;
    int score;
};

int main() {
    std::vector<Student> students = {
        {"Alice", 85},
        {"Bob", 92},
        {"Charlie", 78},
        {"David", 92}
    };

    // 按分数降序排列
    std::sort(students.begin(), students.end(),
        [](const Student& a, const Student& b) {
            return a.score > b.score;
        });

    // 按分数降序,分数相同按名字升序
    std::sort(students.begin(), students.end(),
        [](const Student& a, const Student& b) {
            if (a.score != b.score)
                return a.score > b.score;
            return a.name < b.name;
        });

    // 输出结果
    for (const auto& s : students) {
        std::cout << s.name << ": " << s.score << "\n";
    }

    return 0;
}

核心要点std::sort的Lambda必须满足「严格弱序」——即比较操作必须是可传递的,且不能出现a<b和b<a同时为true的情况。否则程序会崩溃。我见过一个同事写的排序Lambda里用了<=,结果排序时直接段错误。

4.4 三者对比与选择

算法 作用 Lambda返回值 典型场景
std::for_each 遍历每个元素 void 打印、累加、修改
std::find_if 查找第一个匹配 bool 按条件搜索
std::sort 排序 bool(a是否在b前) 自定义排序规则

你想想看,这三个算法覆盖了日常开发中80%的数据处理需求。遍历、查找、排序——哪个项目能少了它们?

4.5 知识结构图

Lambda + STL算法 std::for_each 遍历每个元素,执行操作 Lambda返回void 典型:打印、累加、修改 std::find_if 查找第一个匹配元素 Lambda返回bool 典型:按条件搜索 std::sort 自定义排序规则 Lambda返回bool(a在b前?) 典型:多字段排序 核心:Lambda让STL算法更简洁、更直观

4.6 避坑指南

我这些年用Lambda配合STL算法,踩过不少坑。挑几个典型的说说:

  • 捕获方式搞错:按值捕获时,Lambda内部拿到的是副本。如果你期望修改外部变量,记得用引用捕获[&]
  • 生命周期问题:如果Lambda要在函数返回后使用(比如存到回调列表里),千万别捕获局部变量的引用——那个变量早就销毁了。
  • 排序比较器不严格std::sort要求比较操作是「严格弱序」。用<=>=都会出问题。记住:只用<>
  • 忘记检查find_if结果std::find_if返回的迭代器一定要和end()比较后再使用。这是新手最容易犯的错误。

我的习惯:写Lambda时,先想清楚「我需要捕获哪些变量?按值还是按引用?」然后再动笔。这样能避免一半以上的bug。

好了,关于Lambda与STL算法的结合,我们就聊到这里。这三个算法——for_eachfind_ifsort——是日常开发中的主力军。掌握了它们,你的C++代码会变得更简洁、更易读、更不容易出错。

公众号:蓝海资料掘金营,微信deep3321