lambda表达式——捕获

lambda表达式

等价于匿名函数对象,又称为“闭包”(closure),更便捷,表达更直接。表达式要素包括:
1:捕获列表
2:参数列表
3:mutable修饰符,表达传值或传引用
4:noexcept
5:返回值类型声明 ->
6:表达式体{...}

lambda表达式可接受参数,可返回值,可模板化,也可以通过传值或传引用从闭包范围内访问变量。
编译器将lambda表达式编译为具名函数对象

捕获列表

lambda表达式,从闭包作用域捕获变量而获得状态,分为传值和传引用。
捕获变量登记与函数对象中的示例数据成员。
 [=] 值捕获所有变量
 [&] 引用捕获所有变量
 [&x] 引用捕获x变量
 [x]  值捕获x
 [=,&x] 默认值捕获,x变量通过引用捕获
 [&,x] 默认引用捕获,x通过值捕获
 [this] 捕获当前对象,可访问所有共有成员,C++20中不允许隐式捕获this
 [=,x],[&,&x] 错误,重复指定
 注意:即便默认要值捕获,全局变量总是使用引用捕获
 使用初始化捕获表达式表达move捕获

示例及分析

#include <iostream>
#include <vector>
using namespace std;

class Point{
public:
    double x;
    double y;
    void print() const {
        std::cout << x<< ", "<<y<<endl;;
    }
};

int number=100;	//全局变量可以在lambda修改,跨越所有,捕获类型管理不到
int main()
{
	Point p1{100,200};	
    Point p2{100,200};

	//=表示值捕获,就是将外部变量拷贝进来,声明的时候,只能用auto,因为是匿名对象
	//如果没有mutable,会报错,不允许修改p1,p2;如果修改捕获对象,就需要mutable
    auto lambda1 = [=] (int n) mutable	//p1,p2为外部变量,实际调用了拷贝构造函数
	{
		p1.x+=n;	//从外部拷贝进来了,此处修改不影响外部p1
        p1.y+=n;
        p2.x+=n;
        p2.y+=n;
        p1.print();	//print()有个const,如果此时p1,p2没修改(即上面4行不存在),如果print没有const,lambda也需要mutable(编译器认为调用函数也可能更改了)
        p2.print();

        number++;	//全局变量直接使用
	};

    //Lambda_Value lambda1(p1,p2);
    
     lambda1(10);	//外部看不到p1,p2,打印110,210
     lambda1(10);	//多执行一次,会改变lambda对象内部的p1,p2,栈上的lambda对象没有销毁,120,220
     p1.print();	//不改变外部的p1,p2,所以此处打印是一样的,100,200
     p2.print();
     cout<<sizeof(lambda1)<<endl;	//没有捕获,就是一个空类,有捕获后,就有32byte
     cout<<sizeof(Point)<<endl;		//
    
	cout<<"lambda1------------"<<endl;

	auto lambda2 = [&] (int n)	//引用捕获,不用mutable,lambda内部修改会传递到外部
	{
        p1.print();
        p2.print();
		p1.x+=n;
        p1.y+=n;
        p2.x+=n;
        p2.y+=n;

        number++;	//全局的执行也有效果
	};

    //Lambda_Ref lambda2(p1,p2);

    lambda2(100);//p1,p2先打印原始值,之后 p1:200,300; p2:200,300
  
    p1.print();	//200,300
    p2.print();

    p1.x+=5;	//p1.x = 205
    p1.y+=5;	//p1.y = 305
    p2.x+=5;
    p2.y+=5;
    lambda2(100);	//引用捕获,外部更改也会影响引用捕获内部变量,先打印205,305,之后305,405

    //return lambda2;	//隐蔽错误,因为lambda2是引用捕获,函数返回后,p1,p2销毁了,值捕获就没有问题
    cout<<number<<endl;	//lambda内部修改此处表现出来了
    cout<<sizeof(lambda2)<<endl;//只是引用,大小不计算引用参数大小


    cout<<"lambda2------------"<<endl;

    auto lambda3 = [=, &p1] ()	//默认值捕获,但p1使用引用捕获
	{
		p1.x++;
        p1.y++;	//p1修改外部更改,306,406
        p2.print();	//值捕获305,405
	};
	lambda3();
    
	//auto lambda4 = [ &, &p1] () 或者 auto lambda4 = [ =, p1] () 都是重复性错误
     auto lambda4 = [ &, p1] ()	//默认引用捕获,p1是值捕获
	{
        p1.print();	
		p2.x++;	//引用捕获,p2:306,406
        p2.y++;
	};
	lambda4();
    
//	auto lambda5 = [p2, &number] ()	//失败,number不能
	auto lambda5 = [p2] ()	//单独对p2值捕获
	{
        p2.print();	//306,406
	};
	lambda5();
    
    auto lambda6 = [&p1] ()	//单独对p1引用捕获
	{
		p1.x++;
        p1.y++;
	};
    lambda6();
}

捕获原理

//引用lambda内存模型,表达式内部p1,p2就是外部的p1,p2
//引用就是同一个地址,本质就是一个别名
 struct Lambda_Ref{
		//引用就是一个指针,就是8byte,引用的大小固定了
     Point& p1;	//生命周期不好管理
     Point& p2;

     Lambda_Ref( Point& p1, Point& p2):p1(p1),p2(p2)
     {

     }
     void operator()(int n) {
         p1.x+=n;
         p1.y+=n;
         p2.x+=n;
         p2.y+=n;
     }
 };

//值捕获生成的代码
struct Lambda_Value{

    Point p1;
    Point p2;

    Lambda_Value(const Point& p1, const Point& p2):p1(p1),p2(p2)	//传值,拷贝
    {

    }
    void operator()(int n) {
        p1.x+=n;
        p1.y+=n;
        p2.x+=n;
        p2.y+=n;
    }
};

编译、结果输出

kongcb@tcu-pc:~/testcode/lambda$ g++ lamba_capture.cpp -o lamba_capture
kongcb@tcu-pc:~/testcode/lambda$ ./lamba_capture                       
110, 210
110, 210
120, 220
120, 220
100, 200
100, 200
32
16
lambda1------------
100, 200
100, 200
200, 300
200, 300
205, 305
205, 305
104
16
lambda2------------
305, 405
306, 406
306, 406
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在 Lambda 表达式中,捕获列表用于指定要捕获的外部变量。捕获列表可以包含以下 3 种方式: 1. 捕获捕获值可以通过在捕获列表中指定变量的方式来完成。在捕获值时,Lambda 表达式会在创建时将指定的变量复制一份,并在函数体中使用这份副本。例如: ```c++ int x = 1; auto func = [x]() { std::cout << x << std::endl; }; ``` 在上面的代码中,Lambda 表达式通过捕获值的方式来捕获变量 x。在 Lambda 表达式中使用的变量 x 是一个拷贝,对其进行修改不会影响原有的变量 x。 2. 捕获引用 捕获引用可以通过在捕获列表中指定变量的方式来完成。在捕获引用时,Lambda 表达式会在创建时将指定的变量的引用传递给函数体。例如: ```c++ int x = 1; auto func = [&x]() { std::cout << x << std::endl; }; ``` 在上面的代码中,Lambda 表达式通过捕获引用的方式来捕获变量 x。在 Lambda 表达式中使用的变量 x 是原有变量的引用,对其进行修改会影响原有的变量 x。 3. 捕获列表 捕获列表可以通过在捕获列表中指定变量的方式来完成。在捕获列表中,可以同时指定多个变量,并且可以使用值或引用的方式进行捕获。例如: ```c++ int x = 1, y = 2; auto func = [x, &y]() { std::cout << x << " " << y << std::endl; }; ``` 在上面的代码中,Lambda 表达式通过捕获列表的方式来捕获变量 x 和 y。变量 x 是以值的方式进行捕获的,变量 y 是以引用的方式进行捕获的。 需要注意的是,在 Lambda 表达式中捕获变量时,要考虑变量的生命周期。如果捕获了一个局部变量,而且这个局部变量已经超出了作用域,那么在 Lambda 表达式执行时可能会引发未定义的行为。为了避免这种情况,可以使用捕获值或捕获引用的方式来避免变量超出作用域的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术的微光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值