在 C++ 中为 Lambda 创建闭包

create a closure for a lambda in c++

提问人:matt richards 提问时间:2/26/2022 最后编辑:matt richards 更新时间:2/26/2022 访问量:253

问:

这是为 lambda 函数创建闭包的正确方法吗

#include <functional>
#include <memory>
#include <string>
#include <iostream>
  
using namespace std;


class A
{
    function<void()> fn(string str){    // (2)
        struct closure {
            int num;
        };
        auto ptr    = make_shared<closure>();
        ptr->num    = 99;

        auto fn     = [=](){
            ptr->num++;
            cout << ptr->num << "  --  " << str << endl;
        };//fn
        return fn;
    }//fn
};//A
  
A a;

int main(){
    auto fn   = a.fn("1");

    fn();
    fn();
        
    auto fn2    = a.fn("2");
        
    fn2();
    fn2();
}//main

查看代码 -> 在线 C++ 编译器

闭包中似乎不需要创建 lambda @(2) 的函数的参数,这真的是真的吗?

使用这种方法有什么注意事项,堆/堆栈 = 奶油,我需要释放内存吗?

在 lambda 中通过引用捕获会产生什么实际影响?

编辑

也许我过度简化了,有时函数看起来像这样

  class A {
  
        void fn(string str){
        
              struct closure {
                    int num;
                    int token;
              };
              auto ptr    = make_shared<closure>();
              
              ptr->num    = 100;
              
              auto fn     = [=](){
              
                    ptr->num++;

                    cout << str << ptr->num << endl;
                    
                    if(ptr->num==105){
                          list.rem(ptr->token);  // <= defined elsewhere
                    }
                    
              };
              
              ptr->token    = list.add(fn); // <= defined elsewhere
        
        }//fn
        
  };//A
C++ Lambda 闭包

评论


答:

0赞 Jarod42 2/26/2022 #1

这是为 lambda 函数创建闭包的正确方法吗?

您的 lambda 可以简化为:

function<void()> fn(string str){
    return [str, i = 99]() mutable {
        ++i;
        std::cout << i << "  --  " << str << std::endl;
    };
}

我需要释放内存吗?

你按值捕获一个,它会在销毁时释放它的记忆,你很好。std::shared_ptr

在 lambda 中通过引用捕获会产生什么实际影响?

捕获局部变量时,通过引用捕获将导致悬空指针。

评论

0赞 matt richards 2/26/2022
何时发生销毁 - 当不再引用 lambda 时?
0赞 Jarod42 2/26/2022
shared_ptrowner 是 lambda,拥有 lambda。因此,当范围 where / are (so in ) 结束时,所有这些东西都会被破坏。std::functionfnfn2main
0赞 Chris Uzdavinis 2/26/2022 #2

看起来它没有错,但就其本质而言,它看起来也像是巨大的矫枉过正。

您可以在不使用 sharerd_ptr 或任何动态内存的情况下完成同样的事情,只需捕获 int 并使 lambda 可变:

std::function<void()> fn(std::string str){    // (2)
    return [=, num = 99]() mutable {
        num++;
        std::cout << num << "  --  " << str << "\n";
    };
}

如果您计划复制 lambda 并让两个副本影响相同的底层数据,或者,您保留该共享指针并用它做更多事情,可能是在返回 lambda 之后,那么您的方法可能更有意义。自定义结构和共享 ptr 很重,我尽可能喜欢简单。

此外,按值/按引用问题也很重要,因为在 lambda 中打印出来,它需要确保它使用的对象继续存在。由于它是一个函数参数,因此当函数返回时,参数将被销毁,因此 lambda 不得存储对它的引用,否则它将是对已破坏的对象的引用,并在调用它时导致未定义的行为。您需要副本来保留字符串,以便在调用 lambda 时保证它有效。str