C++中lambda表达式(匿名函数)

1.1 背景交代

下面代码是我读取kitti stereo2012数据集的主函数代码,这部分代码运行后,自动播放所有数据集

#include <io/kitti_dataset.h>
#include <opencv2/highgui.hpp>
int main() {
    //解析配置
    std::string config_file = "~/Documents/CppProjects/vslam_ws/config/kittistereo.yaml";
    //初始化数据集
    KittiDataset dataset(config_file);//path -> config -> kitti -> private var
    //初始化左右图像、相机、当前帧下标
    cv::Mat left, right;
    StereoCamera cam;
    int frame_id = 0;
    //读取数据帧
    while (dataset.NextStereo(left,right,cam)) {
        //显示加载的数据
        std::cout << "Frame: " << frame_id++
                  << " | Baseline: " << cam.baseline << "m" << std::endl;
        cv::Mat stereo;
        cv::hconcat(left, right, stereo);
        cv::imshow("Stereo", stereo);
        if (cv::waitKey(10) == 27) break;
    }
    return 0;
}


现在我想使用键盘n播放下一组,键盘b返回上一组的功能
于是AI给出了代码

#include <io/kitti_dataset.h>
#include <opencv2/highgui.hpp>
int main() {
    //解析配置
    std::string config_file = "~/Documents/CppProjects/vslam_ws/config/kittistereo.yaml";

    // 按索引加载指定帧(通过重建数据集前进到 idx)
    auto load_frame = [&](int idx, cv::Mat& left, cv::Mat& right, StereoCamera& cam)->bool {
        KittiDataset ds(config_file);
        cv::Mat l, r;
        StereoCamera c;
        for (int i = 0; i <= idx; ++i) {
            if (!ds.NextStereo(l, r, c)) return false;
        }
        left = l; right = r; cam = c;
        return true;
    };

    // 初始化
    cv::Mat left, right;
    StereoCamera cam;
    int cur = 0;
    if (!load_frame(cur, left, right, cam)) return 0;

    // 交互浏览:n 下一组,b 上一组,ESC/q 退出
    while (true) {
        std::cout << "Frame: " << cur
                  << " | Baseline: " << cam.baseline << "m" << std::endl;
        cv::Mat stereo;
        cv::hconcat(left, right, stereo);
        cv::imshow("Stereo", stereo);

        int key = cv::waitKey(0);
        if (key == 27 || key == 'q' || key == 'Q') break; // ESC or q
        if (key == 'n' || key == 'N') {
            if (load_frame(cur + 1, left, right, cam)) {
                ++cur;
            } else {
                std::cout << "已到最后一帧" << std::endl;
            }
        } else if (key == 'b' || key == 'B') {
            if (cur > 0 && load_frame(cur - 1, left, right, cam)) {
                --cur;
            } else {
                std::cout << "已到第一帧" << std::endl;
            }
        }
    }
    return 0;
}

1.2 什么是 Lambda 表达式?

Lambda 表达式(Lambda Expression) 是一种 **定义匿名函数(没有名字的函数)**的语法。你可以把它理解成 “写在一行的临时函数”,不需要单独声明或定义,就能直接使用

基本语法如下:

[capture](parameters) -> return_type {
    function_body;
};

1.3 为什么需要 Lambda?

在写 C++ 时,经常会遇到这种情况:

“我想临时定义一个小功能(比如计算、排序规则、回调函数),但是这个功能只在当前函数里用一次,没必要定义成独立函数。”

以前:

bool compare(const Item& a, const Item& b) 
{ 
     return a.value < b.value; 
}

std::sort(v.begin(), v.end(), compare);

现在用 Lambda:

std::sort(v.begin(), v.end(), [](const Item& a, const Item& b) {
    return a.value < b.value;
});

捕获列表详解

Lambda 通过 [capture] 告诉编译器:
“我要把这些外部变量带进我的小函数里一起用。”

Lambda 可以“捕获”当前作用域里的变量,用三种常见方式:

捕获只对 外部变量 有意义;
Lambda 内部定义的变量和普通函数变量一样。

例如:

int main() {
    std::string config_file = "...";

    auto load_frame = [&](int idx, cv::Mat& left, cv::Mat& right, StereoCamera& cam)->bool {
        KittiDataset ds(config_file);  // ✅ 捕获了匿名函数当前所在作用域中的局部变量 config_file
        ...
    };
}

“当前作用域” 就是整个 main() 函数体;

config_file 是定义在 main() 里的局部变量;

[&] 表示以引用方式捕获当前作用域中能看到的所有变量;

所以 config_file 可以在 Lambda 内直接使用。

也就是说lambda表达式(匿名函数)所在作用域中的所有局部变量,使得在匿名函数内部可以使用这些局部变量

捕获语法
(1)如果不想在匿名函数内部修改当前作用域局部变量的话,就使用值捕获[=]
(2)如果想在匿名函数内部修改当前作用域局部变量的话,就使用引用捕获[&]
(3)如果在匿名函数内部,有的捕获变量想改变,有的捕获变量不想改变,就使用[a,&b]

1.4 回到背景提到的例子

重点看一下load_frame这个lambda表达式

// 按索引加载指定帧(通过重建数据集前进到 idx)
    auto load_frame = [&](int idx, cv::Mat& left, cv::Mat& right, StereoCamera& cam)->bool {
        KittiDataset ds(config_file);
        cv::Mat l, r;
        StereoCamera c;
        for (int i = 0; i <= idx; ++i) {
            if (!ds.NextStereo(l, r, c)) return false;
        }
        left = l; right = r; cam = c;
        return true;
    };


实际上这段 Lambda 完全可以改写为一个普通函数:

bool load_frame(int idx, cv::Mat& left, cv::Mat& right, StereoCamera& cam, const std::string& config_file) {
    KittiDataset ds(config_file);
    cv::Mat l, r;
    StereoCamera c;
    for (int i = 0; i <= idx; ++i) {
        if (!ds.NextStereo(l, r, c)) return false;
    }
    left = l; right = r; cam = c;
    return true;
}

不过这样就必须把 config_file 显式传进去。而 Lambda 通过 [&] 直接引用了它,使得调用更简洁。

1.5 什么时候使用 Lambda?

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐