Lambda 表达式是 C++11 引入的一项重要特性,它提供了一种简洁的方式来创建匿名函数对象,可用于临时的、一次性的函数需求,尤其在使用标准库算法时非常方便。以下从语法、捕获列表、参数列表、返回类型、函数体、使用场景和优缺点几个方面详细介绍 Lambda 表达式。
1.基本语法
Lambda 表达式的基本语法如下:
[capture list] (parameter list) -> return type { function body }
- 捕获列表(capture list):用于指定 Lambda 表达式可以访问的外部变量。
- 参数列表(parameter list):与普通函数的参数列表类似,用于传递参数给 Lambda 表达式。
- 返回类型(return type):可省略,编译器通常能自动推导返回类型。
- 函数体(function body):包含 Lambda 表达式要执行的代码。
2.捕获列表
捕获列表用于指定 Lambda 表达式可以访问的外部变量,有以下几种捕获方式:
- 值捕获
通过值的方式捕获外部变量,Lambda 表达式会在创建时复制这些变量的值。
#include <iostream>
int main() {
int x = 10;
auto lambda = [x]() {
std::cout << "Captured value: " << x << std::endl;
};
lambda();
return 0;
}
在上述代码中,[x] 表示以值的方式捕获变量 x,Lambda 表达式内部使用的是 x 的副本。
- 引用捕获
通过引用的方式捕获外部变量,Lambda 表达式会引用这些变量,而不是复制它们的值。
#include <iostream>
int main() {
int x = 10;
auto lambda = [&x]() {
x = 20;
std::cout << "Modified value: " << x << std::endl;
};
lambda();
std::cout << "Value outside lambda: " << x << std::endl;
return 0;
}
这里 [&x] 表示以引用的方式捕获变量 x,Lambda 表达式内部对 x 的修改会影响到外部的 x。
- 混合捕获
可以同时使用值捕获和引用捕获。
#include <iostream>
int main() {
int x = 10;
int y = 20;
auto lambda = [x, &y]() {
std::cout << "x: " << x << std::endl;
y = 30;
std::cout << "Modified y: " << y << std::endl;
};
lambda();
std::cout << "y outside lambda: " << y << std::endl;
return 0;
}
在这个例子中,x 以值的方式捕获,y 以引用的方式捕获。
- 隐式捕获
可以使用 [=] 表示以值的方式隐式捕获所有外部变量,使用 [&] 表示以引用的方式隐式捕获所有外部变量。
#include <iostream>
int main() {
int x = 10;
int y = 20;
auto lambda1 = [=]() {
std::cout << "x: " << x << ", y: " << y << std::endl;
};
auto lambda2 = [&]() {
x = 30;
y = 40;
std::cout << "Modified x: " << x << ", Modified y: " << y << std::endl;
};
lambda1();
lambda2();
std::cout << "x outside lambda: " << x << ", y outside lambda: " << y << std::endl;
return 0;
}
3.参数列表
参数列表与普通函数的参数列表类似,用于传递参数给 Lambda 表达式。
#include <iostream>
int main() {
auto lambda = [](int a, int b) {
return a + b;
};
int result = lambda(3, 5);
std::cout << "Result: " << result << std::endl;
return 0;
}
在这个例子中,Lambda 表达式接受两个整数参数 a 和 b,并返回它们的和。
4.返回类型
返回类型通常可以省略,编译器会自动推导。但在某些情况下,也可以显式指定返回类型。
#include <iostream>
int main() {
auto lambda1 = [](int a, int b) {
return a + b; // 编译器自动推导返回类型为 int
};
auto lambda2 = [](int a, int b) -> double {
return static_cast<double>(a) / b; // 显式指定返回类型为 double
};
std::cout << "Result 1: " << lambda1(3, 5) << std::endl;
std::cout << "Result 2: " << lambda2(3, 5) << std::endl;
return 0;
}
5.函数体
函数体包含 Lambda 表达式要执行的代码,与普通函数的函数体类似。
#include <iostream>
int main() {
auto lambda = []() {
std::cout << "Hello, Lambda!" << std::endl;
};
lambda();
return 0;
}
6.使用场景
- 作为标准库算法的谓词
Lambda 表达式可以作为标准库算法(如 std::sort、std::find_if 等)的谓词,使代码更加简洁。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
// 使用 Lambda 表达式对向量进行排序
std::sort(numbers.begin(), numbers.end(), [](int a, int b) {
return a < b;
});
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
- 异步编程
在异步编程中,Lambda 表达式可以作为回调函数使用。
#include <iostream>
#include <thread>
// 定义一个异步操作函数,该函数接受一个无参数、无返回值的函数对象作为回调函数
void asyncOperation(std::function<void()> callback) {
// 创建一个新的线程来执行异步操作
std::thread([callback]() {
// 模拟一个耗时的异步操作,这里让线程休眠2秒
std::this_thread::sleep_for(std::chrono::seconds(2));
// 当异步操作完成后,调用传入的回调函数
callback();
// 分离线程,使得该线程在后台独立运行,主线程不需要等待它结束
}).detach();
}
int main() {
// 调用异步操作函数,并传入一个Lambda表达式作为回调函数
asyncOperation([]() {
// 当异步操作完成后,输出提示信息
std::cout << "Async operation completed." << std::endl;
});
// 主线程继续执行,输出提示信息
std::cout << "Main thread continues..." << std::endl;
// 为了确保主线程不会过早结束,让主线程休眠3秒
// 这样可以保证异步操作有足够的时间完成并执行回调函数
std::this_thread::sleep_for(std::chrono::seconds(3));
// 程序正常结束,返回0
return 0;
}
这段代码的主要功能是模拟一个异步操作,在一个新的线程中执行耗时任务,当任务完成后调用回调函数。主线程不会等待异步操作完成,而是继续执行后续代码。为了避免主线程过早结束,主线程会休眠一段时间,确保异步操作有足够的时间完成。
7.优缺点
- 优点
简洁性:可以在需要的地方直接定义函数,无需额外定义命名函数,使代码更加简洁。
灵活性:可以方便地捕获外部变量,根据不同的上下文动态调整行为。
可读性:在一些简单的场景下,使用 Lambda 表达式可以使代码的意图更加清晰。
- 缺点
复杂 Lambda 表达式可读性降低:如果 Lambda 表达式的函数体较为复杂,会使代码的可读性降低。
调试困难:由于 Lambda 表达式是匿名的,在调试时可能会增加一定的难度。