我正在尝试在项目中使用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
打印相同的输出而不同。
(我应该为此发布一个新问题吗?)
答案 0 :(得分:2)
一种可能的解决方案是创建一个单独的变量starter
然后通过引用捕获它,以便lambda更改实际变量:
auto starter = 0;
auto getNum = [&starter]() {
return ++starter;
};
然后你打电话:
tester(getNum);
tester(getNum);
输出结果将是从1
到20
的数字。
答案 1 :(得分:2)
变量不会被重置,它是变量的不同副本。事实上,有一堆副本。第一个是你创建的lambda的状态。第二个是在构造第一个tester
时创建的。您必须记住,它会将收到的可调用内容复制到自身中。因此,tester(std::ref(getNum));
tester(std::ref(getNum));
的每次调用都会启动一系列副本。绕过它的一种方法是将lambda传递到std::reference_wrapper
。
getNum
将复制引用包装器,但所有副本都将引用相同的lambda对象getNum
。
现在,假设您打算创建许多不同的对象,例如std::function
,tester
及其提供的类型擦除是一种合理的操作过程,可以避免可能的代码膨胀。要记住的是不要制作多余的副本。因此tester_wrapper
按价值接受是合法的,但gremlin> g.V(61464).project('father','mother','children').
by(out('father')).
by(out('mother')).
by(__.in('father').fold())
==>[father:v[4344],mother:v[4152],children:[v[8440],v[12536],v[40964200]]]
应该通过引用接受。这样,您只需在API边界处为您需要的地方支付类型擦除费用。
答案 2 :(得分:2)
getNum
正在复制您传递给tester
的参数std::function<int(void)>
。也就是说,std::function<int(void)>
不存储原始getNum
,而是存储副本。
template<typename Callback>
void tester_wrapper(Callback && cb, int counter){
if (counter == 10)
return;
else{
cout << cb() << endl;
tester_wrapper(cb, counter + 1);
}
}
template<typename Callback>
void tester(Callback && cb){
tester_wrapper(std::forward<Callback>(cb), 0);
}
现在没有副本,因为两个函数都通过引用接受参数。
顺便说一下,这个代码可能比其他两个代码更快,因为其他两个代码继续使用std:function
,它有一个虚拟调用或等效函数来调用存储的可调用代码。
希望有所帮助。