C++11 lambda 很棒!
但是缺少一件事,那就是如何安全地处理可变数据。
以下将在第一次计数后给出错误计数:
#include <cstdio>
#include <functional>
#include <memory>
std::function<int(void)> f1()
{
int k = 121;
return std::function<int(void)>([&]{return k++;});
}
int main()
{
int j = 50;
auto g = f1();
printf("%d\n", g());
printf("%d\n", g());
printf("%d\n", g());
printf("%d\n", g());
}
给,
$ g++-4.5 -std=c++0x -o test test.cpp && ./test
121
8365280
8365280
8365280
原因是f1()
返回后,k
超出范围但仍在堆栈中。所以第一次g()
执行没k
问题,但在那之后堆栈被破坏并k
失去了它的价值。
因此,我设法在 C++11 中安全返回闭包的唯一方法是在堆上显式分配关闭变量:
std::function<int(void)> f2()
{
int k = 121;
std::shared_ptr<int> o = std::shared_ptr<int>(new int(k));
return std::function<int(void)>([=]{return (*o)++;});
}
int main()
{
int j = 50;
auto g = f2();
printf("%d\n", g());
printf("%d\n", g());
printf("%d\n", g());
printf("%d\n", g());
}
在这里,[=]
用于确保共享指针被复制,而不是被引用,以便正确完成内存处理:当生成的函数超出范围时,k
应该释放堆分配的副本。g
结果如愿,
$ g++-4.5 -std=c++0x -o test test.cpp && ./test
121
122
123
124
通过取消引用来引用变量是非常难看的,但是应该可以使用引用来代替:
std::function<int(void)> f3()
{
int k = 121;
std::shared_ptr<int> o = std::shared_ptr<int>(new int(k));
int &p = *o;
return std::function<int(void)>([&]{return p++;});
}
实际上,这奇怪地给了我,
$ g++-4.5 -std=c++0x -o test test.cpp && ./test
0
1
2
3
知道为什么吗?考虑到共享指针的引用可能是不礼貌的,因为它不是跟踪引用。我发现将引用移动到 lambda 内部会导致崩溃,
std::function<int(void)> f4()
{
int k = 121;
std::shared_ptr<int> o = std::shared_ptr<int>(new int(k));
return std::function<int(void)>([&]{int &p = *o; return p++;});
}
给予,
g++-4.5 -std=c++0x -o test test.cpp && ./test
156565552
/bin/bash: line 1: 25219 Segmentation fault ./test
无论如何,如果有一种方法可以通过堆分配自动安全地返回闭包,那就太好了。例如,如果有一个替代方案[=]
,[&]
表明变量应该在堆上分配并通过对共享指针的引用来引用。当我了解到时,我最初的想法std::function
是它创建了一个封装闭包的对象,因此它可以为闭包环境提供存储,但我的实验表明这似乎没有帮助。
我认为 C++11 中安全可返回的闭包对于使用它们至关重要,有谁知道如何更优雅地实现这一点?