LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

lambda表达式

admin
2025年12月17日 20:54 本文热度 1230

1. Lambda 表达式的基本结构

// 最基本的 lambda 表达式
[]() {
// 函数体
};

这就是一个完整的 lambda 表达式,相当于一个匿名函数

2. 逐部分讲解

① 捕获列表 []

定义 lambda 可以访问哪些外部变量

int a = 10;
int b = 20;

// 1. 不捕获任何变量
[]() { /* 不能访问 a 或 b */ };

// 2. 按值捕获(复制)
[a, b]() { 
// 可以访问 a 和 b 的副本
    std::cout << a + b;  // 输出 30
};

// 3. 按引用捕获
[&a, &b]() {
    a = 100;  // 修改原始变量
    b = 200;
};

// 4. 捕获所有变量(按值)
[=]() {
// 可以访问所有外部变量的副本
};

// 5. 捕获所有变量(按引用)
[&]() {
// 可以访问并修改所有外部变量
};

// 6. 混合捕获
[=, &a]() {  // a 按引用,其他按值
    a = 100;  // 可以修改 a
// b 是副本,不能修改原始 b
};

[&, a]() {  // a 按值,其他按引用
// a 是副本,其他变量可修改
};

② 参数列表 ()

和普通函数一样

// 无参数
[]() { return42; };

// 带参数
[](int x, int y) { return x + y; };

// 自动推导参数类型 (C++14+)
[](auto x, auto y) { return x + y; };

// 模板参数 (C++20+)
[]<typename T>(T x, T y) { return x + y; };

③ 函数体 {}

和普通函数体一样

[]() {
intsum = 0;
for (int i = 1; i <= 10; i++) {
sum += i;
    }
returnsum;  // 返回 55
};

④ 返回类型(通常自动推导)

// 自动推导返回类型
[](int x, int y) { return x + y; };  // 返回 int

// 显式指定返回类型
[](int x, int y) -> double { 
return (x + y) / 2.0
};  // 返回 double

// 多个 return 语句时,需要显式指定
[](bool flag) -> int {
if (flag) {
return1;  // int
    } else {
return0.5;  // double,编译器会困惑
// 需要指定返回类型为 double 或 auto
    }
};

⑤ mutable(可选)

允许修改按值捕获的变量

int counter = 0;

// ❌ 错误:不能修改按值捕获的变量
[=]() {
// counter++;  // 编译错误
};

// ✅ 正确:使用 mutable
[=]() mutable {
    counter++;  // 可以修改副本,但不影响原始变量
return counter;
};

3. 从简单到复杂的示例

示例1:最简单的 lambda

// 定义一个 lambda 并立即调用
auto result = []() {
return"Hello, Lambda!";
}();  // 注意这里的 () 表示立即调用

std::cout << result;  // 输出: Hello, Lambda!

示例2:作为变量存储

// 定义一个 lambda 函数
auto add = [](int a, int b) {
return a + b;
};

// 像普通函数一样使用
int sum = add(1020);  // sum = 30
std::cout << sum << std::endl;

// 另一个 lambda
auto greet = [](const std::string& name) {
return"Hello, " + name + "!";
};

std::cout << greet("Alice") << std::endl;  // Hello, Alice!

示例3:捕获外部变量

cpp

int x = 5;
int y = 3;

// 捕获外部变量
auto multiply = [x, &y]() {
// x 是按值捕获(副本)
// y 是按引用捕获(原始变量)
int result = x * y;
    y = 100;  // 可以修改 y,因为是引用
return result;
};

std::cout << multiply() << std::endl;  // 输出: 15
std::cout << y << std::endl;          // 输出: 100(y 被修改)
std::cout << x << std::endl;          // 输出: 5(x 未被修改)

示例4:在 STL 算法中使用

cpp

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

intmain(){
    std::vector<int> numbers = {12345678910};

// 1. 使用 lambda 过滤偶数
auto it = std::remove_if(numbers.begin(), numbers.end(), 
        [](int n) {
return n % 2 == 0;  // 如果是偶数,移除
        });
    numbers.erase(it, numbers.end());

// 2. 使用 lambda 排序(降序)
    std::sort(numbers.begin(), numbers.end(), 
        [](int a, int b) {
return a > b;  // 降序排序
        });

// 3. 使用 lambda 转换
    std::vector<int> squares;
    std::transform(numbers.begin(), numbers.end(), 
                   std::back_inserter(squares),
                   [](int n) {
return n * n;
                   });

// 4. 使用 lambda 打印
    std::for_each(squares.begin(), squares.end(),
        [](int value) {
            std::cout << value << " ";
        });

return0;
}

示例5:实际应用场景

cpp

// 场景1:按钮点击事件(模拟)
classButton {
public:
voidsetOnClick(std::function<void()> handler){
        onClick = handler;
    }

voidclick(){
if (onClick) onClick();
    }

private:
    std::function<void()> onClick;
};

intmain(){
    Button btn;
int clickCount = 0;

// 使用 lambda 作为事件处理器
    btn.setOnClick([&clickCount]() {
        clickCount++;
        std::cout << "按钮被点击了!次数: " << clickCount << std::endl;
    });

    btn.click();  // 输出: 按钮被点击了!次数: 1
    btn.click();  // 输出: 按钮被点击了!次数: 2

return0;
}

cpp

// 场景2:线程任务
#include<thread>
#include<vector>

intmain(){
    std::vector<std::thread> threads;
    std::mutex mtx;

// 创建 5 个线程,每个线程执行不同的任务
for (int i = 0; i < 5; i++) {
        threads.emplace_back([i, &mtx]() {
            std::lock_guard<std::mutex> lock(mtx);
            std::cout << "线程 " << i << " 正在运行" << std::endl;
        });
    }

// 等待所有线程完成
for (auto& t : threads) {
        t.join();
    }

return0;
}

4. 学习路径建议

第1步:从最简单开始

cpp

// 先写一个什么都不做的 lambda
[]() {};

// 添加简单的返回
[]() { return 42; };

// 添加参数
[](int x) { return x * 2; };

第2步:练习捕获

cpp

int outside = 10;

// 尝试不同的捕获方式
[outside]() { /* 使用 outside 的副本 */ };
[&outside]() { /* 使用 outside 的引用 */ };
[=]() { /* 按值捕获所有 */ };
[&]() { /* 按引用捕获所有 */ };

第3步:在实际场景中使用

cpp

// 1. 排序
std::sort(vec.begin(), vec.end(), 
    [](auto a, auto b) { return a < b; });

// 2. 查找
auto it = std::find_if(vec.begin(), vec.end(),
    [](int x) { return x > 10; });

// 3. 条件变量(你的问题)
cv.wait(lock, [&]() { return !queue.empty(); });

5. 常见错误和注意事项

cpp

// ❌ 错误1:引用捕获局部变量(生命周期问题)
std::function<void()> createLambda() {
intlocal = 42;
return [&local]() {  // 危险!local 将被销毁
        std::cout << local;
    };
}

// ✅ 修正:按值捕获
std::function<void()> createLambda() {
intlocal = 42;
return [local]() {  // 安全:复制了 local
        std::cout << local;
    };
}

// ❌ 错误2:在 lambda 中修改按值捕获的变量(没有 mutable)
int x = 10;
[=]() {
// x++;  // 编译错误
};

// ✅ 修正:使用 mutable
[=]() mutable {
    x++;  // 可以修改副本
};

// ❌ 错误3:复杂的 lambda 导致代码难读
// 如果 lambda 太长,考虑写成普通函数

6. 技巧和最佳实践

cpp

// 技巧1:给 lambda 起一个有意义的类型名
using CompareFunc = bool(*)(intint);
CompareFunc lessThan = [](int a, int b) { return a < b; };

// 技巧2:使用 auto 避免复杂的类型声明
auto add = [](auto a, auto b) { return a + b; };

// 技巧3:lambda 可以作为返回值
automakeMultiplier(int factor){
return [factor](int x) { return x * factor; };
}

auto timesTwo = makeMultiplier(2);
std::cout << timesTwo(5);  // 输出: 10

记住:lambda 表达式就是一个没有名字的函数,可以方便地写在需要函数的地方。 刚开始可能会觉得奇怪,但用多了就会发现它非常方便!


阅读原文:原文链接


该文章在 2025/12/18 9:35:34 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved