lambda的捕获变量被重置 [英] lambda's captured variable is reset

查看:63
本文介绍了lambda的捕获变量被重置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在项目中使用 lambda ,但是我想我缺少关于闭包范围的信息。我测试了这段代码,以某种方式简化了我的问题。

I'm trying to use lambdas inside a project but I think I'm missing something about the closure's scope. I tested this piece of code which in some way is a simplification of my problem.

#include <iostream>
#include <functional>

using namespace std;

void tester_wrapper(std::function<int(void)> cb, int counter){
    if (counter == 10)
        return;
    else{
        cout << cb() << endl;
        tester_wrapper(cb, counter + 1);
    }
}

void tester(std::function<int(void)> cb){
    tester_wrapper(cb, 0);
}

int main()
{
    auto getNum = ([](int starter) {
        return [starter]() mutable {
            return ++starter;
        };
    })(1);

    tester(getNum);
    tester(getNum);
}

首次调用 tester 捕获的变量 starter 被重置,以便相同的输出被打印两次。

After the first call to tester the captured variable starter is reset so that the same output is printed twice.

我该怎么办为了避免lambda的内部计数器( starter )的这种行为?本质上,第二次调用 tester 的呼叫必须打印从12而不是2开始的10个数字。

What should I do in order to avoid this behaviour of the inner counter(starter) of the lambda? Essentially the second call to tester has to print 10 numbers starting from 12 instead of 2.

编辑

感谢您的回答。我没有考虑过要将副本传递给 tester_wrapper ,所以我找到了以下解决方案:

Thank you for the answers. I have not considered that I was passing a copy to tester_wrapper, so I've found this solution:

#include <iostream>
#include <functional>

using namespace std;

std::function<int(void)> mylambda(int starter){
    return [starter]() mutable {
        return ++starter;
    };
}

void tester_wrapper(const std::function<int(void)>& cb, int counter){
    if (counter == 10)
        return;
    else{
        cout << cb() << endl;
        tester_wrapper(cb, counter + 1);
    }
}

void tester(const std::function<int(void)>& cb){
    tester_wrapper(cb, 0);
}

int main()
{
    /*auto getNum = ([](int starter) {
        return [starter]() mutable {
            return ++starter;
        };
    })(1);*/

    auto getNum = mylambda(1);

    tester(getNum);
    tester(getNum);
}

但是,现在我不明白为什么旧的 getNum 使用外部函数( mylambda )输出相同的输出,但输出相同。

However, now I can't understand why the old getNum print the same output while it's different using an "external" function, which is mylambda.

(我应该为此发布一个新问题吗?)

(Am I supposed to post a new question for this?)

推荐答案

该变量未重置,这是变量的另一个副本。实际上,有一堆副本。第一个处于您创建的lambda的状态。第二个是在构造第一个 std :: function 时创建的。您必须记住,它将接收到的可调用对象复制到其自身中。因此,每次 tester 的调用都会启动一连串的副本。解决该问题的一种方法是在 std :: reference_wrapper

The variable isn't reset, it's a different copy of the variable. In fact, there are a bunch copies. The first is in the state of the lambda you create. The second is created when the first std::function is constructed. You must remember that it copies the callable it receives into itself. So each invocation of tester starts a chain of copies. One way to get around it, is to pass the lambda inside a std::reference_wrapper.

tester(std::ref(getNum));
tester(std::ref(getNum));

将复制引用包装,但所有副本将引用同一个lambda对象, getNum

The reference wrapper will be copied, but all copies will refer to the same lambda object, getNum.

现在,假设您打算创建许多不同的对象,例如 getNum std :: function 及其提供的类型擦除是避免可能的代码膨胀的合理做法。要记住的是不要创建多余的副本。因此,按值接受 tester 是合法的,但 tester_wrapper 应该代替引用。这样一来,您只需在API边界的所需位置购买类型擦除即可。

Now, assuming you intend to create many different objects like getNum, a std::function and the type erasure it provides are a reasonable course of action to avoid possible code bloat. The thing to remember is to not create superfluous copies. So tester accepting by value is legitimate, but tester_wrapper should accept by reference instead. That way, you'll only pay for the type erasure in the one place you need it, at the API boundary.

这篇关于lambda的捕获变量被重置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆