Lambda 表达式是 C++11 引入的一项重要特性,它允许我们定义匿名函数对象(闭包),非常适合用作回调函数。下面我将详细介绍 C++ 中 lambda 表达式的语法和使用方法。
基本语法
Lambda 表达式的基本形式如下:
[capture](parameters) -> return_type {
// 函数体
}
组成部分
捕获列表 (capture):定义 lambda 表达式可以访问的外部变量
参数列表 (parameters):与普通函数的参数列表类似
返回类型 (return_type):可选的,通常可以自动推导
函数体:包含 lambda 表达式的代码
捕获列表详解
捕获列表决定了 lambda 表达式如何访问外部变量:
[]
:不捕获任何变量[=]
:以值的方式捕获所有外部变量[&]
:以引用的方式捕获所有外部变量[x]
:只以值的方式捕获 x。按值传递变量,Lambda会复制变量的值。原始变量的修改不会影响Lambda内部的副本。[&x]
:只以引用的方式捕获 x。按引用传递变量,Lambda直接访问原始变量。原始变量的修改会影响Lambda内部的访问。[=, &x]
:以值的方式捕获所有变量,但以引用的方式捕获 x[&, x]
:以引用的方式捕获所有变量,但以值的方式捕获 x
示例代码
基本示例
#include <iostream>
int main() {
// 最简单的 lambda 表达式。auto greet:将 lambda 赋值给一个变量,这样我们可以多次调用它
auto greet = []() {
std::cout << "Hello, World!" << std::endl;
};
greet(); // 调用 lambda
// 带参数的 lambda
auto add = [](int a, int b) {
return a + b;
};
std::cout << "5 + 3 = " << add(5, 3) << std::endl;
// 带捕获的 lambda
int x = 10;
auto increment = [x](int y) { // [x]:以值的方式捕获外部变量 x(创建 x 的副本)
return x + y;
};
std::cout << "10 + 5 = " << increment(5) << std::endl;
return 0;
}
作为回调函数使用
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5}; //std::vector:动态数组,支持快速随机访问。
// 使用 lambda 作为回调函数
std::for_each(numbers.begin(), numbers.end(), [](int n) { //std::for_each:STL 算法,对范围内的每个元素执行操作
std::cout << n * 2 << " ";
});
std::cout << std::endl; //输出:2 4 6 8 10
// 带捕获的 lambda 回调
int threshold = 3;
//std::count_if:计算满足条件的元素数量
auto count = std::count_if(numbers.begin(), numbers.end(),
[threshold](int n) { return n > threshold; });
std::cout << count << " numbers > " << threshold << std::endl; //输出大于 3 的数字数量(2 个:4 和 5)
return 0;
}
可变 lambda(修改值捕获的变量)
#include <iostream>
int main() {
int x = 0;
// 默认情况下,值捕获的变量是只读的
// 使用 mutable 关键字允许修改值捕获的变量
auto increment = [x]() mutable {
x++; // 修改的是副本
std::cout << "Inside lambda: " << x << std::endl;
};
increment();
increment();
std::cout << "Outside lambda: " << x << std::endl;
return 0;
}
每次调用
increment()
,lambda 内部的 x 副本会增加,外部的 x 保持不变输出:
Inside lambda: 1 Inside lambda: 2 Outside lambda: 0
返回类型指定
当 lambda 体包含多条返回语句或返回类型不明显时,可以显式指定返回类型:
#include <iostream>
int main() {
// 显式指定返回类型,显式指定返回类型为 double
auto divide = [](double a, double b) -> double {
if (b == 0.0) {
return 0.0; // 需要显式返回类型,因为可能有不同的返回路径
}
return a / b;
};
std::cout << "10 / 3 = " << divide(10, 3) << std::endl;
std::cout << "10 / 0 = " << divide(10, 0) << std::endl;
return 0;
}
在 STL 算法中的应用
Lambda 表达式与 STL 算法结合使用时非常强大:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {5, 2, 8, 3, 1, 9, 4};
// 排序 - 降序
std::sort(numbers.begin(), numbers.end(), [](int a, int b) {
return a > b;
});
// 查找第一个大于5的数
auto it = std::find_if(numbers.begin(), numbers.end(), [](int n) {
return n > 5;
});
if (it != numbers.end()) {
std::cout << "First number > 5: " << *it << std::endl;
}
// 计算所有偶数的和
int sum = 0;
std::for_each(numbers.begin(), numbers.end(), [&sum](int n) {
if (n % 2 == 0) sum += n;
});
std::cout << "Sum of even numbers: " << sum << std::endl;
return 0;
}
通用 lambda (C++14)
C++14 引入了通用 lambda,可以使用 auto
作为参数类型,。auto x
:参数类型自动推导,可以接受任何类型的参数,编译器会为每种使用到的类型生成对应的函数。
#include <iostream>
int main() {
// 通用 lambda - 参数类型自动推导
auto print = [](auto x) {
std::cout << x << std::endl;
};
print(42); // int
print(3.14); // double
print("Hello"); // const char*
return 0;
}
初始化捕获 (C++14)
C++14 允许在捕获列表中初始化变量:
#include <iostream>
#include <memory>
int main() {
auto ptr = std::make_unique<int>(42);
// 初始化捕获 - 移动 unique_ptr
auto lambda = [value = std::move(ptr)]() {
std::cout << *value << std::endl;
};
lambda();
return 0;
}
value = std::move(ptr)
:在捕获列表中初始化 value使用 std::move 将 unique_ptr 的所有权转移给 lambda
lambda 现在拥有这个指针,外部 ptr 变为 nullptr