lambda表达式
1、概念
定义在代码块中的小型函数对象,可捕获其所在作用域中的变量。其提供了一种便捷的方式来定义和使用短小的、一次性的函数对象,而无需显式定义一个独立的函数或类。
lambda表达式在编译阶段由编译器自动生成一个闭包类,在运行阶段由闭包类产生一个对象,称其为闭包(一个函数对象,匿名且可包含定义时作用域上下文)。
2、基本语法
[capture](parameters) specifiers exception -> return_type { function_body }
1、captures捕获列表
捕获当前函数作用域的零个或多个变量,多个变量间使用逗号分隔;捕获后,可在lambda表达式function_body内使用。
捕获的值必须为局部非静态变量。
(1)值捕获
将函数作用域变量复制到lambda表达式函数对象的内部;
捕获的变量默认类型为常量;
若确实需要改变(“改变”只是function_body内部,值传递的本质没有变化,脱离lambda表达式作用域后,变量的值与传入前一致)捕获的变量,使用mutable修饰;
获取变量的值在定义lambda表达式时已确定,后续修改无效。
(2)引用捕获
值捕获的变量前加&。
(3)特殊方式的捕获
[this]捕获this指针。
[=]按值捕获lambda表达式一定作用域的全部变量值,包含this。
[&]按引用捕获lambda表达式一定作用域的全部变量的引用,包含this。
(4)初始化捕获
捕获表达式赋值给labmda表达式作用域变量的结果:
int var = 5;
auto ret = [expr = var + 1]{ return expr; };[=, *this]使得lambda表达式中保存一份*this的对象副本。
[=, this]只是为了区分与[=, *this]的不同,含义与[=]一致。
2、parameters可选参数列表
与普通函数参数列表一样,不需要时可忽略。
当参数列表中的变量未指定类型,而使用auto修饰时,lambda表达式具备了模板函数的能力,入参根据传入的实参进行推导。
3、specifiers限定符
可选。一般可指定为mutable,表示function_body内改变按值捕获的变量,或调用非const的成员函数。
4、exception异常说明符
可选。使用noexcept来指明lambda不会抛出异常。
5、return_type返回类型
可选。无返回值时为void,有返回值时,可依赖编译器进行推导,不必指定。
无状态的labmda表达式可隐式转换为函数指针,如
最简单的lambda表达式[] { }可作为函数void f(void(*)())的入参,即f([] { })合法。
6、 function_body函数体
与普通函数形式一致。
3、使用场景及示例
1. 作为参数传递给STL算法
在STL算法中,有许多函数接受函数对象作为参数。Lambda表达式可以方便地作为这些函数对象传入,如sort、find_if、replace_if等。
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 查找numbers中第一个大于5的元素
auto it = std::find_if(numbers.begin(), numbers.end(), [](int num) {
return num > 5;
});
2. 作为函数对象
代替函数对象,省略了定义函数对象的步骤,简化了代码。函数对象通常是通过重载类的operator()来实现的,而lambda表达式提供了一种更简洁的语法来实现相同的功能。
auto printNumber = [](int num) {
std::cout << num << std::endl;
};
// 使用标准库算法for_each和lambda表达式遍历向量
std::for_each(numbers.begin(), numbers.end(), printNumber);
3. 简化回调函数
在一些回调函数的场景中,lambda表达式可以直接在调用函数的地方定义,从而避免了定义全局函数或者类成员函数的繁琐步骤。这使得回调函数的使用更加灵活和方便。
#include <iostream>
#include <functional>
#include <chrono>
#include <thread>
class Timer {
public:
using Callback = std::function<void()>;
Timer(int interval, Callback cb)
: interval_(interval), callback_(cb) {}
void start()
{
std::this_thread::sleep_for(std::chrono::seconds(interval_));
callback_();
}
int get_interval() const
{
return interval_;
}
private:
int interval_;
Callback callback_;
};
int main()
{
// 使用lambda表达式定义回调函数
Timer timer(3, []() {
std::cout << "Timer expired! Executing callback." << std::endl;
});
std::cout << "Timer started. Waiting for " << timer.get_interval() << " seconds..." << std::endl;
timer.start();
return 0;
}
4. 多线程编程
在多线程编程中,lambda表达式可以方便地传递给线程对象,从而简化了线程创建和管理的过程。例如,可以使用lambda表达式来定义线程要执行的任务,然后将其传递给线程构造函数或相关函数。
#include <iostream>
#include <thread>
#include <vector>
// 打印线程ID和消息
void printThreadInfo(const std::string& message)
{
std::cout << "Thread ID: " << std::this_thread::get_id() << " " << message << std::endl;
}
int main()
{
constexpr int numThreads = 5;
std::vector<std::thread> threads;
// 使用lambda表达式创建并启动线程
for (int i = 0; i < numThreads; ++i) {
threads.emplace_back([i]() {
printThreadInfo("Hello from thread " + std::to_string(i));
std::this_thread::sleep_for(std::chrono::seconds(1));
printThreadInfo("Thread " + std::to_string(i) + " finished work.");
});
}
for (auto& thread : threads) {
thread.join();
}
return 0;
}
5. 事件处理
在图形界面编程等场景中,lambda表达式可以作为事件处理函数,从而简化了事件处理的逻辑。例如,在GUI框架中,可以将lambda表达式作为事件处理器绑定到特定的按钮或事件上,以响应用户的操作。
#include <iostream>
#include <functional>
class Button {
public:
using ClickHandler = std::function<void()>;
Button(const std::string& label) : label_(label) {}
// 绑定点击事件处理器
void onClick(ClickHandler handler)
{
clickHandler_ = handler;
}
// 模拟按钮点击
void simulateClick()
{
if (clickHandler_) {
clickHandler_();
}
}
private:
std::string label_;
ClickHandler clickHandler_;
};
int main()
{
Button myButton("Click Me");
// 使用lambda表达式绑定点击事件处理器
myButton.onClick([]() {
std::cout << "Button was clicked!" << std::endl;
});
// 模拟用户点击按钮
myButton.simulateClick();
return 0;
}
6. STL容器的遍历
Lambda表达式可以方便地在STL容器中进行遍历操作,提高了代码的可读性和简洁性。例如,可以使用std::for_each算法结合lambda表达式来遍历容器中的元素,并对每个元素执行特定的操作。
std::list<int> lst = {1, 2, 3, 4, 5};
// 使用std::accumulate和lambda表达式计算总和
int sum = std::accumulate(lst.begin(), lst.end(), 0, [](int acc, int x) {
return acc + x;
});
7. 自定义排序和比较规则
当对自定义类型的数据集合进行排序时,需要根据自定义类型的不同属性去实现不同的排序方法。Lambda表达式可以方便地定义这些排序和比较规则,并将其传递给sort等算法函数。
// 使用lambda表达式对自定义类型的数据集合进行排序。
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
struct Person {
std::string name;
int age;
double height;
};
int main()
{
std::vector<Person> people = {
{"Alice", 30, 5.5},
{"Bob", 25, 6.0},
{"Charlie", 35, 5.8},
{"David", 28, 5.9}
};
// 按年龄排序
std::sort(people.begin(), people.end(), [](const Person& a, const Person& b) {
return a.age < b.age;
});
// 按身高排序
std::sort(people.begin(), people.end(), [](const Person& a, const Person& b) {
return a.height < b.height;
});
// 按名字排序
std::sort(people.begin(), people.end(), [](const Person& a, const Person& b) {
return a.name < b.name;
});
return 0;
}
原文地址:https://blog.csdn.net/gaopeng1111/article/details/142798612
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!