首页 > 解决方案 > 如何管理一个自引用的跨范围 lambda?

问题描述

假设我有一个功能,我想在计时器完成时调用它。我已将该功能放入 lambda 函数中。此外,在该函数中,我可能希望设置另一个计时器以在以后的另一个场合调用相同的 lambda。

void doSetupThingsInSomeDecoupledCodeOrWhatever() {
    std::function<void(float)> semiRecursiveFunc;
    semiRecursiveFunc = [&semiRecursiveFunc](float deltaT){
        if (whatever()) {
            // Do something...
        }
        else {
            // Do something else, then:
            float durationMS = getRNGSystem().getUniformFloat(1000.0f, 5000.0f)
            // Gives the timer a duration to wait, and a function to run at the end of it.
            getTimerSystem().setNewTimer(durationMS, semiRecursiveFunc);
        }
    };

    float durationMS = getRNGSystem().getUniformFloat(1000.0f, 5000.0f)
    // Gives the timer a duration to wait, and a function to run at the end of it.
    getTimerSystem().setNewTimer(durationMS, fooLambda);
}

现在,显然这不起作用,因为semiRecursiveFunc它与 的范围有关doSetupThingsInSomeDecoupledCodeOrWhatever,并且当计时器系统尝试运行它时,该功能将不再存在,并且一切都将分解为壮观的火焰球。

管理此问题的最佳方法是什么?据我所知,我不能存储semiRecursiveFunc在指针中,因为不能像那样声明 lambda。对于这种持久性 lambda 用例,是否有一些常用工具?什么是最丑陋的方法,周围的基础设施最少?是否有最佳实践可以遵循,我错过了一些相关工具?任何建议或建议将不胜感激。

标签: c++lambdaclosuresc++20

解决方案


您正在寻找的是y-combinator,有时称为定点组合器

无论哪种方式,都不要使用std::function(这会增加不必要的开销),而是像这样编写回调:

auto semiRecursiveCallback = combinator([](auto self, float deltaT){
    if (whatever()) {
        // Do something...
    }
    else {
        // Do something else, then:
        float durationMS = getRNGSystem().getUniformFloat(1000.0f, 5000.0f)
        // Gives the timer a duration to wait, and a function to run at the end of it.
        // NB: we pass 'self' as the argument
        getTimerSystem().setNewTimer(durationMS, self);
    }
});

我的链接答案或优秀的 Boost.HOF 库combinator的实现在哪里。y_combinatorboost::hof::fix

组合器确保对象本身可以访问自己,因此您可以执行递归操作。在上面的代码中,你实际上得到了一个自己的副本,但这很好:值语义就像那样很酷。


推荐阅读