将输入参数传递给lambda函数中的std :: function

时间:2014-01-11 05:54:20

标签: c++ lambda std-function

我有一个有这样一个公共std::function成员的课程:

class B
{
public:
    B(std::function<void(void)> _func = NULL) : m_function(_func) { }
    std::function<void()> m_function;
};

我有一个成员函数X的课程SomeFunction

class X
{
public:
    void SomeFunction(const std::string & _s)
    {
        //...
    }
};

我还有一个类A,它返回B类型的对象,如下所示:

class A
{
private:
    X x;
public:
    B GetObjectB()
    {
        std::string local = "abc";
        return B(([&]() -> void { x->SomeFunction(local); }))
    }
};
然后我执行

A a;
B b = a.GetObjectB();
b.m_function();

代码正常工作,直到调用函数SomeFunction,并且输入参数_s没有值,尽管已将local作为其输入参数。

我在这里做错了什么?

2 个答案:

答案 0 :(得分:3)

你告诉你的lambda默认通过引用捕获([&]),它正在捕获堆栈上local的引用。作为堆栈变量,当范围退出时,这将被释放,因此返回的对象的my_function已捕获了悬空引用。试试[=]

答案 1 :(得分:3)

这段代码

std::string local="abc";
return B(([&]()->void{x->SomeFunction(local);}))

通过引用捕获所有使用的变量,并返回该函数。捕获的变量中有局部变量local,其范围在范围退出时结束,即从函数返回时。因此,当稍后执行lambda时, local 变量不再存在,这意味着捕获的引用无效(“悬空引用”)。

引自http://en.cppreference.com/w/cpp/language/lambda

  

悬空参考

     

如果通过引用隐式或显式捕获实体,并且   在调用之后调用闭包对象的函数调用操作符   实体的生命周期已经结束,发生了未定义的行为。 C ++   闭包不会延长捕获的引用的生命周期。

本段指出在其他支持闭包的语言中(例如在JavaScript中),通过引用捕获的变量的生命周期是扩展,而在C ++中并非如此。

变量x存在同样的问题,它是调用函数的类A的成员变量。虽然您的测试中可能没有有问题的代码,但这也可能成为一个问题:

B b;

// some other context
{
    A a;
    b = a.GetObjectB();
}

// The lambda previously returned from a.GetObjectB() is now
// stored in b. But it still refers to the now dead a.x!

b.m_function();    // BOOM!

一个简单的解决方法是简单地不通过引用捕获所有变量,而是按值捕获,因此变量将被复制到lambda实例中:

std::string local="abc";
return B(([=]()->void{x->SomeFunction(local);}))

当您不想复制值时,可能的解决方法在我对一个非常相似的问题的回答中进行了讨论:https://stackoverflow.com/a/20928847/592323