C++ lambda表达式

C++ lambda表达式

功能

C++11引入了lambda表达式,lambda表达式支持你定义一个局部的匿名函数,通常这个函数是一次性使用的

格式

[capture-list](parameter)mutable->return-type{statement}
  • parameter:参数列表,对应有名函数的形参,不需要参数可以省略
  • return-type:返回值,可以省略,省略后返回值类型由编译器自动推导,若不省略返回值则参数列表也不能省略
  • statement:函数体,不能省略
  • capture-list:捕捉列表,不能省略

注意:lambda表达式是有类型的,类型大多数情况是随机的,调用lambda表达式需要使用一个变量接收lambda表达式,通常与auto配合使用

最简单的lambda表达式

int main()
{
	auto i = [] {std::cout << "lambda" << std::endl; };
	i();//调用lambda表达式
 
	//运行结果:
	//lambda
	return 0;
}
  • 除了格式以外,lambda表达式和有名函数都差不多,都有参数返回值函数体,唯一不同的是捕捉列表和mutable

lambda表达式实现swap

int main()
{
	auto Swap = [](int& x, int& y){
		int temp = x;
		x = y;
		y = temp;
	};
	int x = 10, y = 20;
	Swap(x, y);
	std::cout << "x = " << x << " , y = " << y << std::endl;
	//运行结果:
	//x = 20 , y = 10
	return 0;
}

捕捉列表和mutable

捕捉列表:指定lambda函数体内可以访问的外部变量。

  • 如果是值捕捉,那么捕捉的是外部变量的拷贝,修改拷贝不会影响外部变量
  • 如果是引用捕捉,那么捕捉的就是外部变量本身,修改捕捉变量就是修改外部变量
  • 值捕捉时,捕捉到的变量默认是无法被修改的,如果需要修改则需要加上mutable
  • 引用捕捉时,捕捉到的变量默认可以被修改,加不加mutable均可
  • 如果需要加上mutable,则必须要带上参数列表,哪怕参数列表为空

捕捉方式:

  • [a,b] 传值捕捉
  • [&a,&b] 传引用捕捉
  • [=] 传值捕捉方式父作用域中所有变量(包括this指针)
  • [&] 传引用捕捉方式父作用域中所有变量(包括this指针)
  • 混合使用,例如捕捉[&x,y]

值捕捉:

int main()
{
	int x = 10, y = 20;
	//值捕捉,捕捉的时x和y的拷贝,默认x和y无法被修改,加上mutable则可以修改x和y
	auto print = [x, y]()mutable{
		x = 1;
		y = 2;
		std::cout << "x = " << x << " , y = " << y << std::endl;
	};
	print();
	//运行结果:
	//x = 1, y = 2
	return 0;
}

引用捕捉:

int main()
{
	int x = 10, y = 20;
	//引用捕捉,捕捉的是外部变量本身,默认可以被修改
	auto Swap = [&x, &y] (){
		int temp = x;
		x = y;
		y = temp;
	};
	Swap();
	std::cout << "x = " << x << " , y = " << y << std::endl;
	//运行结果:
	//x = 20 , y = 10
	return 0;
}

捕捉列表的其他捕捉方式

  • [=]表示的是以值捕捉的方式捕捉lambda的父作用域的所有变量
  • [&]表示的是以引用捕捉的方式捕捉lambda的父作用域的所有变量
int main()
{
	int x = 10, y = 20;
	//值捕捉方式捕捉main函数中的所有变量
	auto print1 = [=]()mutable{
		x = 1;
		y = 2;
		std::cout << "x = " << x << " , y = " << y << std::endl;
	};
	//引用捕捉方式捕捉main函数中的所有变量
	auto print2 = [&](){
		x = 1;
		y = 2;
		std::cout << "x = " << x << " , y = " << y << std::endl;
	};
	print1();
	print2();
	//运行结果:
	//x = 1, y = 2
	//x = 1, y = 2
	return 0;
}
  • 混合捕捉:lambda表达式支持一部分外部变量传值捕捉,一部分外部变量引用捕捉

特例:

  • [=, &a, &b]:使用值传递方式捕获所有变量,除了 a 和 b,这两个变量将以引用方式捕获。
  • [&,a, this]:使用引用传递方式捕获变量 a 和 this,其他变量将以值传递方式捕获。
  • [=, a]:这个捕捉列表是错误的,因为 = 已经以值传递方式捕获了所有变量,而 a 也被重复捕获了。
  • 在块作用域以外的lambda函数:捕捉列表必须为空。这意味着lambda不能捕获任何外部变量。
  • 在块作用域中的lambda函数:只能捕获父作用域中局部变量。
int main(){
    int a = 0;
	int b = 1;
	int c = 2;
	int d = 3;
	const int e = 1;
	cout << &e << endl;
 
	// 引用的方式捕捉所有对象,除了a
	// a用传值的方式捕捉
	auto func = [&, a] {
		//a++;
		b++;
		c++;
		d++;
		//e++;
		cout << &e << endl;//const & 得到的是同一个地址
	};
 
	func();
 
	return 0;
}

lambda表达式的原理

仿函数:

#include <iostream>
 
class Add {
public:
    int operator()(int a, int b) const {
        return a + b;
    }
};
 
int main() {
    Add add;
    int result = add(10, 20);
    std::cout << "Result: " << result << std::endl;
    return 0;
}
  • 仿函数汇编底层
    在这里插入图片描述

lambda底层使用的是用仿函数实现的

auto print = [] {
008123AF  xor         eax,eax  
008123B1  mov         byte ptr [ebp-0D5h],al  
		std::cout << "Hello , World" << std::endl;
	};
 
	print();
008123B7  lea         ecx,[print]  
008123BA  call        <lambda_0596717148ff8a7e4509ccb4cd363cfe>::operator() (0812190h) 
  • 汇编层面,调用lambda最关键的一步是call一个类型的operator(),实际上跟仿函数是一摸一样的,这个仿函数类型是lambda_+字符串,字符串是由系统的算法随机生成的,这个随机生成的字符串重复概率很低

lambda表达式的赋值

lambda表达式之间不能相互赋值

  • 每一个lambda表达式(哪怕函数体一模一样),调用时底层的仿函数类都不相同,所以无法让两个lambda表达式之间相互赋值
  • 如果需要再获得一个lambda表达式的变量,可以以原来接收的变量为模板,auto生成一个相同类型的变量
  • 如果某个容器/函数中需要传一个仿函数的类型,那么你可以使用decltype,lambda变量生成的仿函数类的默认构造是被禁掉的,但拷贝构造没有被禁掉
int main()
{
	//1、print1和print2无法相互赋值
	auto print1 = [] {
		std::cout << "Hello , World" << std::endl;
	};
	auto print2 = [] {
		std::cout << "Hello , World" << std::endl;
	};
	//2、以auto生成一个print1变量的副本
	auto print1_temp = print1;
 
	//3、以decltype把comp_less的类型传给func
	//lambda变量的默认构造被delete了,需要使用拷贝构造传参
	auto comp_less = [](int* x, int* y) {
		return *x < *y;
	};
	std::priority_queue<int*, std::vector<int*>, decltype(comp_less)> pq(comp_less);
	return 0;
}
C++ Lambda表达式是一种简洁的匿名表示方法,可以在代码中定义并使用。Lambda表达式的格式一般为:[] (参数列表) mutable -> 返回值类型 { 表达式 }。Lambda表达式可以捕获外部变量,并将其作为参数传递给函数体部分进行处理。Lambda表达式在使用时可以作为函数对象、函数指针或者函数参数进行传递。 Lambda表达式的底层原理是通过生成一个匿名类来实现。该类会重载函数调用运算符(),并包含Lambda表达式的函数体。Lambda表达式中捕获的外部变量会以成员变量的形式存储在该类中。当Lambda表达式被调用时,实际上是调用了该类的重载函数调用运算符()。 Lambda表达式可以与std::function结合使用,以实现函数对象的灵活使用。也可以将Lambda表达式赋值给相同类型的函数指针,实现函数指针的使用。但一般不建议这样使用,因为Lambda表达式已经提供了更加方便和简洁的方式。 总结来说,C++ Lambda表达式是一种用于定义匿名函数的语法,可以捕获外部变量并进行处理。其底层通过生成一个匿名类来实现,并提供了与std::function和函数指针的结合使用方式。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [C++11:lambda表达式](https://blog.csdn.net/zhang_si_hang/article/details/127117260)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值