当前位置: 首页 > article >正文

[C++11] Lambda 表达式

lambda 表达式(Lambda Expressions)作为一种匿名函数,为开发者提供了简洁、灵活的函数定义方式。相比传统的函数指针和仿函数,lambda 表达式在简化代码结构、提升代码可读性和编程效率方面表现出色。


Lambda 表达式的基本语法

在 C++ 中,lambda 表达式的格式如下:

[capture-list] (parameters) -> return type {
    function body
}

各部分含义:

  • **[capture-list]**:捕捉列表,指定lambda表达式可以访问的外部变量。捕捉列表,该列表总是出现在 <font style="color:rgb(31,35,41);">lambda</font> 函数的开始位置,编译器根据[]来判断接下来的代码是否为 <font style="color:rgb(31,35,41);">lambda</font> 函数,捕捉列表能够捕捉上下⽂中的变量供 <font style="color:rgb(31,35,41);">lambda</font> 函数使⽤,捕捉列表可以传值传引⽤捕捉,捕捉列表为空也不能省略
  • **(parameters)**:参数列表,类似普通函数的参数,如果不需要参数传递,则可以连同<font style="color:rgb(31,35,41);">( )</font>⼀起省略。
  • **-> return type**:返回值类型。通常可以省略,由编译器推导。
  • **function body**:函数体,实现具体的功能。
// 1、捕捉为空也不能省略
// 2、参数为空可以省略
// 3、返回值可以省略,可以通过返回对象⾃动推导
// 4、函数题不能省略
auto func1 = []
{
    cout << "hello bit" << endl;
    
    return 0;
};

示例代码

以下是一个简单的 lambda 表达式示例:

auto add = [](int x, int y) -> int {
    return x + y;
};
std::cout << add(3, 5) << std::endl; // 输出:8

捕捉列表的使用

捕捉列表的分类

捕捉列表可以在 lambda 表达式中允许访问外部作用域的变量。捕捉方式主要包括:

  1. 显式捕捉:在捕捉列表中显⽰的传值捕捉和传引⽤捕捉,捕捉的多个变量⽤逗号分割,例如在捕捉列表中使用传值[x, y]或传引用[&z]捕捉变量。
  2. 隐式捕捉:使用 =(按值捕捉)或 &(按引用捕捉)进行隐式捕捉,这样我们 lambda 表达式中⽤了哪些变量,编译器就会⾃动捕捉那些变量,底层汇编也是这样的。
  3. 混合捕捉:可混合显式与隐式捕捉,<font style="color:rgb(31,35,41);">[=, &x]</font>表⽰其他变量隐式值捕捉,x引⽤捕捉;<font style="color:rgb(31,35,41);">[&, x, y]</font>表⽰其他变量引⽤捕捉,x和y值捕捉。当使⽤混合捕捉时,第⼀个元素必须是&或=,并且&混合捕捉时,后⾯的捕捉变量必须是值捕捉,同理=混合捕捉时,后⾯的捕捉变量必须是引⽤捕捉。

使用时的注意事项

  1. 全局位置的<font style="color:rgb(31,35,41);">lambda</font>

lambda 表达式如果在函数局部域中,他可以捕捉 lambda 位置之前定义的变量。但是不能捕捉静态局部变量和全局变量,静态局部变量和全局变量也不需要捕捉,lambda 表达式中可以直接使⽤。这也意味着 lambda 表达式如果定义在全局位置,捕捉列表不需要捕捉任何变量,必须为空

// 全局
auto f1 = [](int a, int b){
    return a + b;
}
  1. mutable修饰lambda

默认情况下, lambda 捕捉列表是被const修饰的,也就是说传值捕捉的过来的对象不能修改,mutable加在参数列表的后⾯可以取消其常量性,也就说使⽤该修饰符后,传值捕捉的对象就可以修改了,但是修改还是形参对象,不会影响实参。使⽤该修饰符后,参数列表不可省略(即使参数为空)。

int a = 5, b = 10;
auto func = [=]() mutable {
    a++;         // 可以修改按引用捕捉的变量
    b++;
};

注意:默认情况下捕捉的变量为 const,无法在 lambda 中修改,除非在参数列表后加 mutable 修饰符,如上例中的 mutable

捕捉的具体示例

  1. 显式捕捉:
int a = 0, b = 1, c = 2, d = 3;
auto func1 = [a, &b]
	{
		// 值捕捉的变量不能修改,引⽤捕捉的变量可以修改
		//a++;
		b++;
		int ret = a + b;
		return ret;
	};
cout << func1() << endl;
  1. 隐式值捕捉 / 隐式引用捕捉:
// 隐式值捕捉
// ⽤了哪些变量就捕捉哪些变量
auto func2 = [=] {
		int ret = a + b + c;
		return ret;
	};
cout << func2() << endl;

// 隐式引用捕捉
// ⽤了哪些变量就捕捉哪些变量
auto func3 = [&] {
		a++;
		c++;
		d++;
	};
func3();
cout << a << " " << b << " " << c << " " << d << endl;
  1. 混合捕捉:
// 混合捕捉1
auto func4 = [&, a, b] {
		//a++;
		//b++;
		c++;
		d++;
		return a + b + c + d;
	};
func4();
cout << a << " " << b << " " << c << " " << d << endl;

// 混合捕捉2
auto func5 = [=, &a, &b] {
		a++;
		b++;
		/*c++;
		d++;*/
		return a + b + c + d;
	};
func5();
cout << a << " " << b << " " << c << " " << d << endl;

Lambda 表达式的应用场景

常见应用

  • 排序函数中的比较器:利用 lambda 表达式可简化排序代码。
  • 自定义线程执行函数:lambda 可定义线程任务,便于封装。
  • 智能指针的删除器:lambda 表达式可以方便地作为 unique_ptr 等的自定义删除器。

示例:用于商品排序的 Lambda 表达式

相当于直接替代仿函数来使用。

struct Goods {
    std::string name;
    double price;
    int rating;
    Goods(const std::string &n, double p, int r) : name(n), price(p), rating(r) {}
};

std::vector<Goods> items = {{"苹果", 2.5, 5}, {"橙子", 3.0, 4}, {"香蕉", 1.5, 3}};

// 使用 lambda 表达式按价格升序排序
std::sort(items.begin(), items.end(), [](const Goods &a, const Goods &b) {
    return a.price < b.price;
});

Lambda 表达式的原理

Lambda 表达式在底层通过创建一个仿函数对象来实现。当我们定义一个 lambda 表达式时,编译器会生成一个包含捕捉列表和函数体的匿名类,lambda 表达式实际上是该类的一个 operator(),底层是仿函数对象。

汇编层的实现

通过汇编代码可看到,lambda 表达式生成的对象会自动调用 operator(),并且捕捉的变量会作为构造函数的参数。如下所示:

class Rate
{
public:
	Rate(double rate)
		: _rate(rate)
	{}

	double operator()(double money, int year)
	{
		return money * _rate * year;
	}

private:
	double _rate;
};

int main()
{
	double rate = 0.49;

	// lambda
	auto r2 = [rate](double money, int year) {
		return money * rate * year;
		};

	// 函数对象
	Rate r1(rate);

	r1(10000, 2); // 仿函数
	r2(10000, 2); // lambda


	auto func1 = [] {
		std:: cout << "hello world" << std::endl;
		};
	func1();
	return 0;
}

call的内容不难看出lambda的底层就是仿函数。


http://www.kler.cn/a/387887.html

相关文章:

  • C语言第九周课——经典算法
  • 综合案例铁锅炖(CSS项目大杂烩)
  • 网络安全-Linux基础(bash脚本)
  • 【电力系统】永磁同步电机调速系统带有扰动观测器
  • 吾店云介绍 – 中国人的WordPress独立站和商城系统平台
  • docker基础:搭建centos7(详见B站泷羽sec)
  • 1.1 Android 应用的基础知识
  • w030基于web的甘肃非物质文化网站的设计与开发
  • A15基于Spring Boot的宠物爱心组织管理系统的设计与实现
  • Go的数组,slice切片,map的使用
  • 微服务架构面试内容整理-服务注册与发现-Nacos
  • 【数据库系列】postgresql链接详解
  • 制作python的Dockerfile
  • 梧桐数据库之以识别优质的移动服务套餐为例讲解SQL实现分享
  • Shell扩展
  • vite+vue3项目兼容低版本浏览器
  • 定位,堆叠,CSS精灵,过渡,光标(前端)
  • 软考高级架构 - 8.2 - 系统架构评估 - 超详细讲解+精简总结
  • Linux系统编译boot后发现编译时间与Windows系统不一致的解决方案
  • nginx配置文件介绍及示例
  • 深度学习——多层感知机MLP(一、多层感知机介绍)
  • 设计模式-行为型-常用-2:职责链模式、状态模式、迭代器模式
  • 【安装配置教程】二、VMware安装并配置ubuntu22.04
  • jmeter常用配置元件介绍总结之取样器
  • CDH大数据平台部署
  • 高防服务器和高防IP的区别是什么?