文章目录
C++ Lambda表达式入门:小白也能看懂的匿名函数
Lambda
表达式是C++11引入的匿名函数,可以理解为“临时自定义函数”。它能让你在不定义完整函数的情况下,快速实现简单逻辑。本文将通过实际案例,带你彻底掌握Lambda
的用法。
一、为什么需要Lambda?
假设我们需要对一组数字排序,传统做法需要先定义一个比较函数:
// 传统方式:定义单独的比较函数
bool compare(int a, int b) {
return a > b; // 降序排列
}
int main() {
std::vector<int> nums = {5,2,8,3};
std::sort(nums.begin(), nums.end(), compare); // 传递函数指针
return 0;
}
但这样存在两个问题:
- 比较函数与排序逻辑耦合,复用性差
- 需要为每个小功能都定义函数
Lambda
表达式可以解决这些问题,让代码更紧凑:
std::sort(nums.begin(), nums.end(), int a, int b{
return a > b;
});
二、Lambda的基本结构
Lambda
的语法格式为:
[capture](parameters) -> return_type {
// 函数体
}
各部分含义:
- [] 捕获列表:定义Lambda可以访问的外部变量
- () 参数列表:与普通函数参数写法相同
- -> return_type 返回类型(可选):编译器通常能自动推导
- {} 函数体:包含实际执行的代码
三、实战案例解析
案例1:无捕获的简单Lambda
auto print = {
std::cout << "Hello Lambda!" << std::endl;
};
print(); // 输出:Hello Lambda!
案例2:带参数的Lambda
auto add = int a, int b {
return a + b;
};
std::cout << add(3,5); // 输出:8
案例3:捕获外部变量
int factor = 10;
auto multiply = int x {
return x * factor;
};
std::cout << multiply(5); // 输出:50
四、捕获列表的四种方式
捕获方式 | 说明 | 示例 |
---|---|---|
[] | 不捕获任何变量 | []{} |
[=] | 按值捕获所有外部变量 | [=]{ return x + y; } |
[&] | 按引用捕获所有外部变量 | [&]{ ++x; } |
[var1, &var2] | 混合捕获(值/引用) | [a, &b]{ … } |
示例:捕获列表的实际应用
int x = 10, y = 20;
// 按值捕获x,按引用捕获y
auto calc = {
y += 5; // 修改外部变量y
return x * y;
};
std::cout << calc(); // 输出:10*25=250
std::cout << y; // 输出:25(被修改)
五、实际应用场景
场景1:STL算法中使用
std::vector<std::string> words = {"apple", "orange", "banana"};
// 按字符串长度排序
std::sort(words.begin(), words.end(), const auto& a, const auto& b{
return a.size() < b.size();
});
// 输出结果:apple banana orange
for (auto& word : words) {
std::cout << word << " ";
}
场景2:多线程任务
include <thread>
int main() {
int data = 42;
// 创建线程并传递Lambda
std::thread t({
std::cout << "Thread ID: " << std::this_thread::get_id()
<< ", Data: " << data << std::endl;
});
t.join();
return 0;
}
场景3:事件回调
// 模拟按钮点击事件
class Button {
public:
using ClickHandler = std::function<void()>;
void setOnClick(ClickHandler handler) {
onClick = handler;
void click() {
if (onClick) onClick();
private:
ClickHandler onClick;
};
int main() {
Button btn;
int count = 0;
// 设置点击事件处理
btn.setOnClick({
std::cout << "Button clicked " << ++count << " times!"
<< std::endl;
});
btn.click(); // 输出:Button clicked 1 times!
btn.click(); // 输出:Button clicked 2 times!
return 0;
}
六、高级技巧
1. 泛型Lambda(C++14+)
auto print = const auto& value {
std::cout << value << std::endl;
};
print(10); // 输出整数
print(3.14); // 输出浮点数
print("Hello"); // 输出字符串
2. 初始化捕获(C++14+)
int x = 10;
auto lambda = {
std::cout << y; // 输出20
};
3. 返回类型推导
auto divide = double a, double b {
if (b == 0) throw std::runtime_error("Divide by zero");
return a / b; // 编译器自动推导返回类型为double
};
七、注意事项
- 变量生命周期:如果按引用捕获变量,需确保被引用变量在Lambda使用时有效
- 性能考虑:按值捕获会产生拷贝,对大型对象建议使用[&]或移动语义
- 类型转换:
Lambda
表达式不能隐式转换为函数指针(除非不捕获任何变量)
总结
特性 | 说明 |
---|---|
匿名函数 | 无需命名,适合一次性使用 |
捕获外部变量 | 通过[]列表灵活控制捕获方式 |
类型推导 | 自动推导参数和返回类型 |
STL 算法搭档 | 简化sort 、for_each 等算法的使用 |
最佳实践:
- 优先使用[=]或[&]简化代码
- 需要修改外部变量时使用引用捕获[&]
- 长生命周期对象使用值捕获[=]
- 复杂逻辑建议还是使用普通函数
掌握Lambda
表达式,能让你的C++
代码更简洁高效,尤其在进行STL
编程和异步操作时,绝对是不可或缺的神器!