C++ lambda表达式

发布于:2025-05-19 ⋅ 阅读:(24) ⋅ 点赞:(0)

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;
}

网站公告

今日签到

点亮在社区的每一天
去签到