c++ - 如何与可变参数同时重用 C++ 闭包——类似于 JavaScript?
问题描述
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() 中,闭包函数 greetingTask() 的工作方式与 JavaScript 中的 greeter() 函数不同。据我了解,当 startGreeterTask() 完成执行时,greetingTask() 和所有引用的参数都超出了范围,因此释放了堆栈内存。第二次调用 startGreeterTask() 会造成堆栈内存的冲突使用,并确保出现段错误。
如何让 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 );
}
推荐阅读
- c++ - 如何在for循环中创建自动迭代,它将为其分配指针而不是值
- powershell - Powershell - 试图获取批量用户的描述字段
- homebrew - 收到“没有这样的文件或目录”错误消息时如何从 brew 中删除 virtualbox
- terminal - Cloudflare 牧马人安装
- file-upload - Azure 逻辑应用:通过 http WebAPI 文件上传发送 PDF 文件时损坏
- c# - “无法绑定到数据源上的属性或列名称”但列确实存在
- java - JAXB - StringWriter 修剪字符串转换的值,但在控制台中打印整个 xml 字符串
- gridview - 如何在显示之前将所有“类似整数”的值转换为附加“.0”?
- c# - AzureStorage 表查询:构建包含筛选器
- javascript - 如何捕获第 3 方 JS 事件以修改其操作?