为啥 C++11 中捕获的变量在捕获中具有不同的值?

Posted

技术标签:

【中文标题】为啥 C++11 中捕获的变量在捕获中具有不同的值?【英文标题】:Why is a captured variable in C++11 of different value inside a capture?为什么 C++11 中捕获的变量在捕获中具有不同的值? 【发布时间】:2020-04-27 10:20:16 【问题描述】:

我不知道如何解释这种行为,这里用一个最小的例子来展示。 为什么没有正确捕获size

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


auto&& matcher1KO = [] (vector<int> &v) 
    int size = v.size();
    cout << "size outside : " << size << "\n";  // print 1
    return [&] (bool b) 
      cout << "v.size() : " << v.size() << "\n"; // print 1
      cout << "size inside : " << size << "\n"; // print 0
    ;
;

auto&& matcher2OK = [] (vector<int> &v) 
    int size = v.size();
    cout << "size outside : " << size << "\n"; // print 1
    return [&] () 
      cout << "v.size() : " << v.size() << "\n"; // print 1
      cout << "size inside : " << size << "\n"; // print 1
    ;
;

int main() 
  vector<int> v +1;

  auto matcherf1 = matcher1KO(v); // 
  matcherf1(true);

  auto matcherf2 = matcher2OK(v);
  matcherf2();

【问题讨论】:

您返回一个 lambda,它通过引用捕获了局部变量 size,并且所述引用变得悬空。您需要按价值捕获。 auto&amp;&amp; matcher1KO 为什么是&amp;&amp; 您返回 auto&amp;&amp; 本地的 lambda,这也是未定义的行为。这些闭包的生命周期不会通过将其绑定到返回类型中的引用来延长。 @rafix07 你的意思是matcher1KOmatcher2OK 是悬空的?他们不绑定到生命周期延长的 lambda 吗?他们绑定的 lambda 按值返回。抱歉,我看不到 UB... @songyuanyao 你说得对,我看错了这些台词。最外面的 lambda 是纯右值,然后它只是作为全局变量matcher1KO 绑定到右值引用,所以这里一切正常。我收回我之前的评论。 【参考方案1】:

两个代码都有未定义的行为,一切皆有可能。

这两种情况的原因是一样的:变量size是lambda的operator()内部的一个局部对象,调用结束时会被销毁。您正在通过引用捕获 size 并且引用被悬空。

将其更改为按值捕获就可以了。例如

return [=] (bool b) 
  cout << "v.size() : " << v.size() << "\n"; // print 1
  cout << "size inside : " << size << "\n"; // print 1
;

【讨论】:

我明白了。为什么编译器不会捕获“未定义的行为”?这真是令人惊讶 @nicolas UB 意味着编译器可以为所欲为;这是程序员的责任。 @nicolas 语言中“存在”未定义行为的全部原因是它涵盖了那些(通常)对于编译器来说太难或不值得花时间的事情诊断。使这些正确是程序员的责任。悬空引用就是这样的教科书案例。 这个 UB 没有任何用处。所以不排除 this UB 是没有意义的。其他一些 UB 可能意味着过于复杂而无法表达或检查编译器。事实并非如此。 @nicolas Clang 对此给出警告。

以上是关于为啥 C++11 中捕获的变量在捕获中具有不同的值?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我无法在具有 void 返回类型的异步函数中捕获异常?

为啥 C# 8.0 中使用声明的变量的闭包不同?

在 Java Lambda 中,为啥在捕获的变量上调用 getClass()

为啥 XCUITest 在 Xcode 11.4.1 中捕获的流失败

C ++ 11 lambda捕获`this`并按值捕获局部变量

在c ++ 11中按值捕获成员变量的好方法是啥? [复制]