如何将C++闭包与变量参数同时重用——类似于JavaScript

How to re-use a C++ closure concurrently with variable arguments -- similar to JavaScript?

本文关键字:类似于 JavaScript 参数 C++ 闭包 变量      更新时间:2023-10-16

JavaScript创建了一个具有创建范围的闭包。例如:

function startGreeter( name, iterations, delay ) {
function greeter() {
console.log("greetings, "+name+"! i="+iterations);
if( --iterations )
setTimeout( greeter, delay );
}
setTimeout( greeter, delay );
}
startGreeter("Alex",3,1000);
startGreeter("Beth",2,1500);

JavaScript为greeter((函数创建了两个不同的闭包实例;一个给亚历克斯,一个给贝丝。当然,我们可以创建数十或数百个这样的实例;例如从数据数组中。

我想在C++中实现同样的功能;特别是能够创建一个函数的N个不同的同时闭包实例。

void startGreeterTask( AsyncManager& asyncManager, std::string name, int iterations, longtime msDelay )
{
TaskFunction greetingTask = [&]()
{
std::cout << "greetings, task " << name << "! iterations = " << iterations << std::endl;
if( --iterations )
{
asyncManager.setTimeout( greetingTask, msDelay );
}
};
asyncManager.setTimeout( greetingTask, msDelay );
}
int main( int argc, char* argv[] )
{
AsyncManager asyncManager;
startGreeterTask( asyncManager, "Alex", 3, 1000 );
startGreeterTask( asyncManager, "Beth", 2, 1500 );
asyncManager.execute();
}

AsyncManager提供了setTimeout((功能,如果我在main((的作用域中创建多个闭包,它就会工作。但是在startGreetingTask((中,闭包函数greetingTasks((的工作方式与JavaScript中的greeter((函数不同。据我所知,当startGreeterTask((完成执行时,greetingTask((和所有引用的参数都超出了范围,因此堆栈内存被释放。第二次调用startGreeterTask((会造成堆栈内存的混合使用,seg错误可以确保。

如何使startGreeterTask((像startGreeter((函数一样工作?

感谢Igor Tandetnik为我指明了正确的方向。我在这里发布了一个完整的答案,供其他人查找。

解决方案是(a(默认情况下使用复制捕获,(b(对我不想复制的类实例使用引用捕获,(c(添加mutable关键字以允许修改捕获的变量,以及(d(避免闭包函数对其本身的引用。现在,闭包不是调用自己,而是调用定义闭包的函数。这种形式的自引用,通过一层间接,失去了JavaScript习惯用法的一点灵活性和表达能力,但它功能齐全,很好地解决了问题。

也就是说,如果其他人解决了这个特定的闭包自引用问题,我希望看到解决方案。

void greetTask( AsyncManager& asyncManager, std::string name, int iterations, longtime msDelay )
{
TaskFunction greetingClosure = [=,&asyncManager]() mutable
{
std::cout << "greetings, task " << name << "! iterations = " << iterations << std::endl;
if( --iterations )
{
greetTask( asyncManager, name, iterations, msDelay );
}
};
asyncManager.setTimeout( greetingClosure, msDelay );
}