骑熊砍怪 2022-06-15 21:38 采纳率: 100%
浏览 316
已结题

C++中lambda表达式的值传递和引用传递到底有什么区别?类中实现与main函数中实现有什么区别?

问题遇到的现象和发生背景

C++中lambda表达式的值传递和引用传递到底有什么区别?

初学C++的时候,学到函数形参可以通过值传递和引用传递这两种方式。如果函数实现中让形参参与了运算,那么如果通过值传递,形参没有发生改变,如果用引用传递,则形参发生改变,此为背景。

然后我在用lambda表达式的时候发现一个神奇的现象,就是如果在类中用ambda表达式,无论值传递和引用传递,都可以改变形参的值。我贴一下代码,

问题相关代码,请勿粘贴截图

class A { //声明类A
public:
int a = 10; //声明变量a
void B1(int a) { //声明一个值传递的普通函数
a += 5;
}

void B2() {     //声明一个函数,用lambda表达式值传递来实现
    [=]() {a += 5; }();
}

};

void main() { //测试
A a1; //分别创建两个对象
A a2;
a1.B1(a1.a); //a1调用B1()后,打印a1.a
cout << "a1中的a=" << a1.a << endl;

a2.B2();        //a2调用B2()后,打印a2.a
cout << "a2中的a=" << a2.a << endl;
运行结果及报错内容

img


可以看到,a1调用B1()后,a=10,没有改变,符合值传递不改变形参值的结论;但是a2调用B2()后,a=15,说明发生了改变,我lambda表达式中无论用‘=’还是‘&‘都不影响结果。

然后继续探究,发现一个更有意思的事情:
就是如果我的lamda不写在类中,直接先在main函数中,则必须用"=";否则报错1.

img

如果改写&则不报错,并且结果是15:

img


img

然后如果声明变量改写为static,lambda用值传递则不报错,并且结果是15:

img


img

然后如果变量不写static,lambda表达式用mutable,则不报错,但结果是10:

img


img

我的解答思路和尝试过的方法
我想要达到的结果

所以我想问一下,为什么会这样。通过写在main函数中的lambda表达式可以知道,lambda其实是区别值传递和引用传递的,值传递确实不改形参,但是如果写在类里,效果等同于引用传递!这是为啥?

  • 写回答

2条回答 默认 最新

  • fortunely2 2022-06-16 08:38
    关注

    因为类成员函数中,用lambda进行隐式捕获,会捕获成员函数的局部变量,即this指针。也就是说,相当于会捕获this指针。不论你用值捕获,还是引用捕获,都可以通过this来访问其数据成员aa。
    也就是说,[=](){aa += 5;}();会相当于下面代码

    // 为避免名称遮掩,下面代码把成员变量a修改为aa
    // class A的成员函数B2,隐含第一个参数是A* this,但这里this是一个右值
        void B2() {     //声明一个函数,用lambda表达式值传递来实现
            auto it = this;
            [it]() { it->aa += 5;}();
        }
    
    // 写成其他形式(值捕获this)
        void B2() {     //声明一个函数,用lambda表达式值传递来实现
            [this]() { this->aa += 5;}();
        }
    // 隐式引用捕获this
        void B2() {     //声明一个函数,用lambda表达式值传递来实现
            [&]() { this->aa += 5;}();
        }
    // 隐式值捕获
        void B2() {     //声明一个函数,用lambda表达式值传递来实现
            [=]() { this->aa += 5;}();
        }
    

    不过,this比较特殊,是一个右值,无法通过显式引用捕获,即[&this](){}不合法。this本身值也无法修改,但成员变量可以修改,因为整个过程你并没有捕获成员变量本身,而是捕获this。

    lambda捕获列表,始终是针对局部变量的。static变量、全局变量,都不受此影响,不需要捕获。
    lambda相当于是匿名函数,(表达式内部)默认不能修改值捕获的变量,如果要修改,必须在参数列表首加上mutable关键字修饰。另外,要在表达式内部改变捕获的值,该值本身必须是可修改的左值。因为通常来说,修改一个值传递给匿名函数的变量,没有意义,并不会影响原值。
    因此,在main函数中用lambda,捕获的也是main函数局部变量。要在表达式内部修改值捕获的c,必须加上mutable,但不会影响原来main中的c的值;要影响原来捕获的c,就不能值捕获,而要引用捕获。
    还有没有其他方法,通过值捕获的方式,修改c?
    有的,可以参考上面值捕获this的方式,通过指针来修改c。

    int c = 10;
    int* p = &c;
    [=]() { ++(*p);} ();
    cout << "c=" << c << endl; // 打印11,而非10
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论 编辑记录
查看更多回答(1条)

报告相同问题?

问题事件

  • 系统已结题 6月24日
  • 已采纳回答 6月16日
  • 赞助了问题酬金50元 6月15日
  • 修改了问题 6月15日
  • 展开全部

悬赏问题

  • ¥15 使用wpf制作打砖块游戏时遇到的一个Bug
  • ¥15 海洋可控源和大地电磁一维联合反演
  • ¥15 MFC多文档程序获取视图指针问题
  • ¥20 解体如何编写程序还有运行结果截图
  • ¥15 如何把matlabR2023遗传算法工具箱里面的各类选项对应的代码调出来呢?
  • ¥30 MDIGetActive() 获取活动view
  • ¥15 有数据,如何安装使用CLIP模型检索图片
  • ¥15 lingo代码报错无法运行,如何解决,如果能解决请提供能够运行的lingo代码
  • ¥100 读取 IMU BNO080 数据
  • ¥15 基于RTKLIB框架写的精密单点定位-AR