C++中,与C#等语言不同的是,可以在lambda表达式中指定是否以值或引用方式捕获封闭作用域变量。这导致了未定义情况,即可能将一个封闭作用域通过引用捕获的lambda传递给在调用lambda表达式之前返回的函数:
void test()
{
int t = 1;
enqueue_task([&]() { do_something(t); });
}
在这种情况下,当Lambda表达式所指定的任务被调度执行时,“t”很可能会超出作用域。显然,这会导致难以解决的错误。
我的解决方案是添加如下语言特性:
template<class T>
void enqueue_task(T lambda)
{
static_assert(!std::is_lambda<T>::value || std::is_lambda_captured_by_value<T>::value,
"The lambda expression is executed asynchronously and therefore capturing eclosing state via reference is forbidden.");
// enqueue task for execution
}
对我而言,这将是一种干净的“非侵入式”扩展,可让中间件编写者保护其API免受误用。当然,它并不能提供防弹保护,因为我仍然可以通过值传递指向堆栈对象的指针,可能还有更多方式。不管怎样,即使通过值传递时仍会默默地导致未定义行为的代码本身可能就是有问题的。
有没有类似的已经支持的解决方案?
对我而言,目前一个明智的解决方案似乎是在延迟执行情况下根本不允许任何lambda表达式。例如,事件处理程序不应该是lambda类型。这是说得容易做起来难,因为这也意味着我不能使用std :: function,而必须回到老式函数类型。
甚至更好的方法是引入一种关键字,比如:
void test()
{
int t = 1;
enqueue_task(deferred () { do_something(t); });
}
这将确保编译器可以,以任何方式,使传递的lambda函数适合延迟执行,这意味着当其封闭范围消失时。
我认为C++11已经走了很长一段路来提高C++编程的安全性。这个lambda东西是你仍然在脚下指着一把枪的少数几个地方之一。它只是一个定时炸弹。