通过引用传递的 Lambda 在构造函数中被调用时会运行,但在后来存储在数据成员中时不会运行。

2023年 8月 20日 77点热度 0人点赞 0条评论

以下的 C++ 代码会先打印 11.1,然后导致崩溃。Lambda 函数在构造函数内部似乎被正确调用,但在稍后,同样的函数不再起作用!为什么会这样?Lambda 函数是否有有限的生命周期?

#include <functional>
#include <iostream>

class LambdaStore
{
public:
    LambdaStore(const std::function<void(float)>& _fn)
    : fn(_fn)
    {
        fn(11.1f);    // 正常工作
    }

    void ExecuteStoredLambda()
    {
        fn(99.9f);    // 导致崩溃
    }

private:
    const std::function<void(float)>& fn;
};

int main()
{
    LambdaStore lambdaStore([](float a) { std::cout << a << '\n'; });

    lambdaStore.ExecuteStoredLambda();
}

回答

你没有存储一个 lambda 函数,而是存储了一个对 std::function 的引用。

实际上,当 lambda 隐式转换为 std::function 时,这个 std::function 会被创建为临时对象。这个 std::function 临时对象在构造函数被调用的行之后就会销毁。

    LambdaStore(const std::function<void(float)>& _fn) // _fn 引用了一个临时对象
    : fn(_fn)
    {
        fn(11.1f);    // 正常工作
    } // fn(以及 _fn)被销毁

然而,即使你将类更改为直接通过模板使用 lambda 类型,lambda 本身也会在后续阶段销毁。但无论类型如何,这对于任何 C++ 类型都是成立的。以 int 为例:

class LambdaStore
{
public:
    LambdaStore(const int& _i)
    : i(_i)
    {
        std::cout << i;    // 正常工作
    }

    void ExecuteStoredLambda()
    {
        std::cout << i;    // 导致崩溃
    }

private:
    const int& i;
};

void main()
{
    LambdaStore lambdaStore(1); // 在此处创建临时 int
    // 临时 int 被销毁

    lambdaStore.ExecuteStoredLambda();
}

当绑定到函数参数时,临时对象不会在创建它们的语句之后获得生命周期延长。

然而,如果只使用花括号,它们会在直接绑定到成员引用时获得生命周期延长:

struct ref {
    const int& i
};

int main() {
  ref a{3};

  std::cout << a.i; // 正常工作

  ref b(3);

  std::cout << b.i; // 导致崩溃
}

解决方案显然是将 std::function 存储为值,而不是引用:

class LambdaStore
{
public:
LambdaStore(const std::function<void(float)>& _fn)
: fn(_fn)
{
fn(11.1f); // 正常工作
}

void ExecuteStoredLambda()
{
fn(99.9f); // 同样会工作
}

private:
std::function<

标签:
最后更新:2023年 8月 20日

admin

这个人很懒,什么都没留下