我正在尝试在项目中使用 lambda
,但我认为我遗漏了有关闭包范围的一些信息。我测试了这段代码,它在某种程度上简化了我的问题。
#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
被重置,因此相同的输出被打印两次。
我应该怎么做才能避免 lambda 的内部计数器 (starter
) 的这种行为?本质上,对 tester
的第二次调用必须打印从 12 而不是 2 开始的 10 个数字。
编辑
感谢您的回答。我没有考虑过将拷贝传递给 tester_wrapper
,所以我找到了这个解决方案:
#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
)却有所不同。
(我应该为此发布一个新问题吗?)
最佳答案
变量没有被重置,它是变量的不同拷贝。事实上,有一堆拷贝。第一个是您创建的 lambda 的状态。第二个是在构造第一个 std::function
时创建的。您必须记住,它将接收到的可调用对象复制到自身中。因此每次调用 tester
都会启动一个拷贝链。绕过它的一种方法是在 std::reference_wrapper
中传递 lambda。 .
tester(std::ref(getNum));
tester(std::ref(getNum));
将复制引用包装器,但所有拷贝都将引用同一个 lambda 对象,getNum
。
现在,假设您打算创建许多不同的对象,如 getNum
、std::function
和它提供的类型删除是避免可能的合理操作过程代码膨胀。要记住的是不要创建多余的拷贝。所以 tester
按值接受是合法的,但 tester_wrapper
应该改为按引用接受。这样一来,您只需在 API 边界需要的地方为类型删除付费。
关于c++ - lambda 的捕获变量被重置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48241935/