【问题标题】:c++ When/Why are variables captured by value constructed/destroyedc ++何时/为什么变量被构造/销毁的值捕获
【发布时间】:2020-10-01 06:05:32
【问题描述】:

我无法理解调用 C(const C&) 和 ~C() 时发生的情况

所以,我制作了一个按值捕获的 lambda,然后返回它。 据我了解,每个返回的 lambda 都有自己的上下文 (痕迹与该声明一致)。

然而,根据 stroustrup 将 lambda 描述为函数对象快捷方式 (在 C++ 第 4 版的 11.4.1 中),我希望 one 复制 only 为捕获 变量,而且似乎不是这样。

这是我的代码

//g++  5.4.0
#include <functional>
#include <iostream>
using namespace std;

class C{
    float f; 
  public:
    C(float f): f(f){ cout << "#build C(" << f << ")\n"; }
    C(const C& c): f(c.f+0.1){ cout << "#copy C(" << f << ")\n"; }
    ~C(){ cout << "#destroy C(" << f << ")\n"; }
    void display() const { cout << "this is C(" << f << ")\n"; }
};

std::function<void(void)> make_lambda_val(int i){
    C c{i};
    return [=] () -> void { c.display(); } ;
}

int main(){
    cout << "/**trace\n\n";
    cout << "---  ??  ---\n";
    {
        auto l0 = make_lambda_val(0);
        auto l1 = make_lambda_val(1);
        auto l2 = make_lambda_val(2);
        cout << "ready\n";
        l0();
        l1();
        l2();
    }
    cout << "\n*/\n";
}

以及相应的痕迹:(附上我的评论)

/**trace

---  ??  ---
#build C(0)
#copy C(0.1)     <<--|  2 copies ??
#copy C(0.2)     <<--|  
#destroy C(0.1)  <----  one of which is already discarded ?
#destroy C(0)
#build C(1)
#copy C(1.1)
#copy C(1.2)
#destroy C(1.1)
#destroy C(1)
#build C(2)
#copy C(2.1)
#copy C(2.2)
#destroy C(2.1)
#destroy C(2)
ready
this is C(0.2)   <---- the second copy is kept ?
this is C(1.2)
this is C(2.2)
#destroy C(2.2)
#destroy C(1.2)
#destroy C(0.2)

*/

【问题讨论】:

    标签: c++ lambda


    【解决方案1】:

    我希望只为捕获的变量制作一份副本

    实际上捕获的变量被复制一次。也就是说,它只是一次复制操作中的源。

    std::function 不是 lambda。它的初始化涉及复制可调用对象。因此,当 lambda 被复制到 std::function 中时,它按值保存的变量也会被复制。当函数返回时,lambda 临时变量被销毁。您看到的是您创建的 lambda 中的变量的破坏。

    【讨论】:

      【解决方案2】:

      来自 lambda 生成函数 make_lambda_val(int)std::function&lt;void(void)&gt; 返回类型为此分析添加了额外的复制复杂性层,这解释了您在跟踪输出中标记的额外复制。

      为简化起见,通过将 make_lambda_val(int) 函数完全替换为以下 lambda 生成 lambda 对象(范围在 main())来替换额外的 std::function 层:

      auto make_lambda_val = [](int i){
          C c(i);
          return [=] () -> void { c.display(); };
      };
      

      在这种情况下,您的跟踪输出看起来符合预期:

      /**trace
      
      ---  ??  ---
      #build C(0)
      #copy C(0.1)
      #destroy C(0)
      #build C(1)
      #copy C(1.1)
      #destroy C(1)
      #build C(2)
      #copy C(2.1)
      #destroy C(2)
      ready
      this is C(0.1)
      this is C(1.1)
      this is C(2.1)
      #destroy C(2.1)
      #destroy C(1.1)
      #destroy C(0.1)
      
      */
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-07
        • 1970-01-01
        • 2016-02-11
        • 1970-01-01
        相关资源
        最近更新 更多