C ++ 11:防止lambda范围捕获错误

时间:2014-02-06 17:46:46

标签: c++11 lambda

在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-thing是你仍然用枪指着你的脚的少数几个地方之一。它只是一个滴答作响的定时炸弹。

1 个答案:

答案 0 :(得分:4)

通常,补救措施是按值[=]() {...}捕获 当复制实际对象不可行时,通常可以通过shared_ptr使用它,这可能会提供更便宜的复制,具体取决于上下文,并且还允许您共享所有权,以便调用者和延迟的lambda独立使用它。

C ++ 14应该具有移动捕获语义,这将解决在不需要共享时复制对象的性能问题。

否则,传递-ref就是你想要的。就像C ++中的所有内容一样,不仅仅是在lambda中,当你开始传递指针和引用时,你需要小心。