C++ lambda[&]捕获指针变量引用还是指针值?兼谈C++20捕获变更
C++20 Lambda隐式捕获
this的弃用与捕获机制解析 问题背景
以下代码在C17中可正常运行,但在C20中会触发警告:
void BehaviorClass::refreshDisplay() { // ...remove all widgets from m_container... for (auto x : listOfData) { auto w = new QPushButtonSubclass(x); // displays the data QObject::connect(w, &QAbstractButton::click, [=](){ onButtonClick(w); }); // 触发C++20警告的lambda m_container->layout()->addWidget(w); } m_container->update(); } void BehaviorClass::onButtonClick(QPushButtonSubclass* w) { // ...do something with w... }
开发者疑惑C++20弃用[=]隐式捕获this的原因,同时对[&]的捕获行为存在疑问。
核心疑问解答
1. C++20为何弃用[=]隐式捕获this?
在C11至C17中,[=]会隐式复制捕获this指针——因为lambda内调用成员函数onButtonClick需要通过this访问。但这种隐式捕获非常隐蔽,开发者很容易忽略lambda持有了this的副本:当BehaviorClass对象被销毁后,lambda内的this会变成悬空指针,触发未定义行为。
C++20要求显式写出[=, this]或[this],本质是强制开发者明确感知到lambda捕获了this,避免无意识的生命周期风险,提升代码可读性与安全性。
2. [&]捕获的是局部变量w的引用还是指针值?
[&]捕获的是局部变量w本身的引用,而非w指向的堆对象或指针值。
w是循环内的栈局部变量,每次迭代会复用栈内存位置(即w是同一个栈变量,仅值被覆盖)。用[&]捕获后,lambda持有的是对该栈变量的引用:
- 循环迭代时,
w的值被更新为指向新按钮,所有之前创建的lambda都会“看到”最新的w值,导致点击任何按钮都会传入最后一个创建的按钮指针; - 当
refreshDisplay函数执行完毕,栈帧销毁,w的生命周期结束,lambda内的引用会变成悬空引用,触发未定义行为。
3. 为什么[&]会有生命周期相关讨论?
生命周期问题的核心在于捕获的是栈变量的引用:栈变量的生命周期仅局限于当前函数/迭代,当栈帧销毁后,引用指向的内存会被回收复用,此时lambda再访问该引用就会读取无效内存。
即便w指向的是堆对象(生命周期由new/delete管理),也无法改变w本身是栈变量的事实——[&]捕获的是栈上的指针变量,而非堆对象。
4. 关于this捕获的变化细节
this本身是指向当前对象的指针:
- C++17及之前,
[=]会隐式复制this,lambda持有this的副本; - C++20中,这种隐式捕获被弃用,必须显式声明
[=, this](效果与旧版[=]完全一致),或根据需求选择[&, this](按引用捕获this,需确保对象生命周期长于lambda)。
推荐写法
针对示例代码,最安全清晰的写法有两种:
- 显式复制捕获所需变量(推荐,最小化捕获范围):
[w, this](){ onButtonClick(w); }
- 显式声明复制捕获
this(与C++17[=]行为一致):
[=, this](){ onButtonClick(w); }
内容的提问来源于stack exchange,提问作者Tustin2121
相关产品推荐
相关产品推荐

