C++ 的 lambda 表达式 是一种简洁的方式,用于定义匿名函数或可调用对象。它通常用于需要短小、内联定义的函数,尤其适用于 STL 算法、并行编程、事件处理等场景。Lambda 表达式的语法灵活且强大,可以捕获外部变量、指定参数类型、返回值类型等。
1. Lambda 表达式的基本语法:
[捕获列表](参数列表) -> 返回类型 { 函数体 }
- 捕获列表
[ ]
:指定 Lambda 表达式如何捕获外部变量。可以通过值捕获(复制外部变量)或者引用捕获(直接引用外部变量)等方式。 - 参数列表
( )
:指定函数的输入参数。 - 返回类型
->
:指定 Lambda 表达式的返回类型,通常情况下,C++ 可以自动推导返回类型,因此可以省略->
部分。如果需要,可以显式指定返回类型。 - 函数体
{ }
:Lambda 表达式的代码块,执行实际的操作。
2. 捕获外部变量(捕获列表)
Lambda 表达式可以捕获外部作用域中的变量(即定义在 Lambda 外部的变量),这使得 Lambda 能够在局部环境中使用这些外部变量。
- 按值捕获:
[x]
复制外部变量x
的值到 Lambda 中。 - 按引用捕获:
[&]
按引用捕获所有外部变量,使得 Lambda 内部能够修改外部变量。 - 混合捕获:可以同时按值和按引用捕获不同的变量,例如
[x, &y]
表示按值捕获x
,按引用捕获y
。 - 捕获
this
指针:[this]
可以捕获当前对象的this
指针,用于在 Lambda 表达式中访问类的成员变量或成员函数。
3. 示例代码
3.1 基本 Lambda 表达式
#include <iostream>
using namespace std;
int main() {
// 一个不带捕获的 Lambda,接受两个整数参数并返回它们的和
auto add = [](int a, int b) -> int {
return a + b;
};
cout << "Sum: " << add(3, 5) << endl; // 输出: Sum: 8
return 0;
}
3.2 捕获外部变量
#include <iostream>
using namespace std;
int main() {
int x = 10, y = 20;
// 按值捕获 x 和按引用捕获 y
auto lambda = [x, &y]() {
cout << "Captured by value: " << x << endl; // x 的值是 10
cout << "Captured by reference: " << y << endl; // y 的值是 20
y += 5; // 修改 y 的值
};
lambda();
cout << "After lambda, y = " << y << endl; // 输出: After lambda, y = 25
return 0;
}
- 在上面的例子中,
x
按值捕获,而y
按引用捕获。调用 Lambda 后,y
的值被修改,而x
的值保持不变。
3.3 捕获 this
指针(在类成员函数中使用)
#include <iostream>
using namespace std;
class MyClass {
public:
MyClass(int val) : value(val) {}
void print() {
// 捕获 this 指针
auto lambda = [this]() {
cout << "Value from class: " << value << endl;
};
lambda();
}
private:
int value;
};
int main() {
MyClass obj(42);
obj.print(); // 输出: Value from class: 42
return 0;
}
- 在上面的代码中,Lambda 表达式通过捕获
this
指针访问类的成员变量value
。捕获this
使得 Lambda 可以访问当前对象的成员。
3.4 返回类型推导与显式返回类型
- 自动推导返回类型:Lambda 表达式可以省略返回类型,C++ 会自动推导。
- 显式指定返回类型:可以通过
->
语法显式指定 Lambda 的返回类型。
#include <iostream>
using namespace std;
int main() {
// 自动推导返回类型
auto multiply = [](int a, int b) {
return a * b;
};
cout << "Product: " << multiply(3, 4) << endl; // 输出: Product: 12
// 显式指定返回类型
auto divide = [](int a, int b) -> double {
return static_cast<double>(a) / b;
};
cout << "Division: " << divide(5, 2) << endl; // 输出: Division: 2.5
return 0;
}
- 第一个
multiply
Lambda 没有显式返回类型,编译器根据a * b
的类型自动推导出返回值为int
。 - 第二个
divide
Lambda 显式指定了返回类型为double
,即使a
和b
是int
类型,返回值类型也是double
。
4. Lambda 表达式的应用场景
-
STL 算法:很多 STL 算法(如
std::for_each
,std::sort
,std::transform
等)都可以接受 Lambda 表达式作为参数,用于指定如何处理集合中的元素。示例:
#include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> vec = {1, 2, 3, 4, 5}; // 使用 Lambda 表达式打印每个元素 for_each(vec.begin(), vec.end(), [](int x) { cout << x << " "; }); cout << endl; return 0; }
-
事件处理和回调函数:Lambda 表达式可以用作回调函数(如 GUI 程序中的事件处理、并发编程中的线程回调等)。
-
并行编程:在并行编程中,Lambda 使得任务划分和分配变得简单和直观。
#include <iostream> #include <thread> using namespace std; int main() { // 使用 Lambda 创建一个新线程 thread t([]() { cout << "Hello from thread!" << endl; }); t.join(); // 等待线程结束 return 0; }
Lambda是如何捕获的?
C++ 中,Lambda 捕获外部变量是基于作用域,而不是类型或最近距离。捕获变量是按照 作用域(即变量定义的范围)来决定的,而不是基于变量类型或物理位置的距离。
关键点:
- Lambda 捕获变量是基于外部作用域中可访问的变量。
- 如果捕获的变量与 Lambda 作用域内的其他变量同名,Lambda 会捕获 最外层作用域 中的变量,而不是局部作用域的同名变量。
- Lambda 捕获是 静态的,即在编译时确定,而不是动态判断的。
#include <iostream>
void example() {
int x = 10; // 局部变量 x
// Lambda 捕获外部变量 x
auto lambda1 = [x]() { // 捕获外部 x
std::cout << "lambda1 captured x (by value): " << x << std::endl;
};
{
int x = 20; // 内部作用域中的局部变量 x
// Lambda 捕获外部 x
auto lambda2 = [x]() { // 捕获外部 x
std::cout << "lambda2 captured x (by value): " << x << std::endl;
};
lambda1(); // 输出 lambda1 捕获的外部作用域的 x
lambda2(); // 输出 lambda2 捕获的内部作用域的 x
}
// 内部作用域结束,lambda2 销毁
lambda1(); // 输出 lambda1 捕获的外部作用域的 x
}
int main() {
example();
return 0;
}
lambda1
捕获的是函数外层作用域的x
(值为 10)。lambda2
在内层作用域中定义,它捕获的是内层作用域中的x
(值为 20)。尽管在lambda2
定义时,外层作用域的x
仍然是存在的,Lambda 会捕获其定义时作用域中的x
。这证明了捕获变量是基于作用域,而不是距离或类型。
进一步解释:
- 作用域规则:
lambda1
捕获的是外层作用域中的x
(值 10),而lambda2
捕获的是它定义时的内层作用域中的x
(值 20)。 - 捕获规则:Lambda 在定义时,根据它的作用域来决定捕获什么变量。捕获的变量不是基于类型或物理距离,而是 基于编译时的作用域。
如果外部作用域有多个变量,Lambda 可以通过捕获列表显式指定需要捕获哪些变量。捕获列表允许你选择性地捕获特定的变量,并且可以按值或按引用捕获。
#include <iostream>
void example() {
int a = 10, b = 20;
// 捕获 a 按值,b 按引用
auto lambda = [a, &b]() {
std::cout << "Captured a by value: " << a << std::endl;
std::cout << "Captured b by reference: " << b << std::endl;
b += 10; // 修改 b
};
lambda();
// 输出修改后的 b
std::cout << "After lambda, b = " << b << std::endl;
}
int main() {
example();
return 0;
}