c++ 通过引用传递的Lambda在构造函数中调用时运行,但在以后存储在数据成员中时不运行

zlwx9yxi  于 2023-08-09  发布在  其他
关注(0)|答案(4)|浏览(75)

下面的C++代码打印11.1然后崩溃。lambda函数似乎在构造函数中被正确调用,但后来,同一个函数不再起作用!为什么会这样呢?Lambda的寿命有限吗?

#include <functional>
#include <iostream>

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

    void ExecuteStoredLambda()
    {
        fn(99.9f);    // crashes
    }

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

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

    lambdaStore.ExecuteStoredLambda();
}

字符串

0dxa2lsx

0dxa2lsx1#

你不是在存储lambda,你是在存储对std::function的引用。
实际上,当lambda被隐式转换为std::function时,std::function被创建为临时变量。该std::function临时在调用构造函数的行之后死亡。

LambdaStore(const std::function<void(float)>& _fn) // _fn refers to a temporary
    : fn(_fn)
    {
        fn(11.1f);    // works
    } // fn (and _fn) dies

字符串
但是,即使你通过模板直接使用lambda类型来改变你的类,lambda本身也会死,但这对任何C++类型都是正确的,不管是什么类型。考虑int s:

class LambdaStore
{
public:
    LambdaStore(const int& _i)
    : i(_i)
    {
        std::cout << i;    // works
    }

    void ExecuteStoredLambda()
    {
        std::cout << i;    // crashes
    }

private:
    const int& i;
};

void main()
{
    LambdaStore lambdaStore(1); // temporary int created here
    // temporary int gone

    lambdaStore.ExecuteStoredLambda();
}


当临时对象绑定到函数参数时,它们的生存期不会超过为其创建的语句。
如果它们直接绑定到成员引用,则它们确实会获得生存期扩展,但仅使用大括号时:

struct ref {
    const int& i
};

int main() {
  ref a{3};

  std::cout << a.i; // works

  ref b(3);

  std::cout << b.i; // crashes
}


解决方案显然是通过值而不是引用来存储std::function

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

    void ExecuteStoredLambda()
    {
        fn(99.9f);    // will also work
    }

private:
    std::function<void(float)> fn; // no & here
};

5kgi1eie

5kgi1eie2#

lambda函数
这可能是你的理解误入歧途的地方。Lambda是对象成员函数;它们本身不是函数。它们的定义看起来像函数体,但实际上是对象的调用操作符operator()的定义。
你对这个场景的评估的一个半修正版本:

  • lambda对象似乎在构造函数中正确地调用了它的运算符,但后来,同一个对象不再工作!*

为什么只有“半”纠正?因为在LambdaStore中,您不能直接访问lambda对象。相反,您可以通过std::function对象(引用)访问它。更正确的版本:

**std::function*对象似乎在构造函数中正确调用了它的运算符,但后来,同一个对象不再工作!

如果我把lambda的概念去掉,也许这会更清楚?你的main函数基本上是以下内容的语法捷径。

struct Functor {
    void operator()(float a) {
        cout << a << endl;
    }
};

int main()
{
    LambdaStore lambdaStore(Functor{});

    lambdaStore.ExecuteStoredLambda();
}

字符串
在这个版本中,应该更容易看到您创建了一个临时对象作为LambdaStore构造函数的参数。(实际上,您创建了两个临时对象--显式Functor对象和隐式std::function<void(float)>对象。)然后您可能会注意到,您存储了一个引用,该引用在构造函数完成后立即变为悬空引用。
Lambda的寿命有限吗?
是的,在C++中,具有非静态存储持续时间的对象具有有限的生命周期。

avwztpqn

avwztpqn3#

是的,临时变量(包括lambda)的生命周期是有限的,引用并不能保持它的生命周期。您可能需要存储一个副本。您的问题与存储引用的任何其他临时变量(如int)相同。被引用的变量必须比引用的寿命长,才能在引用的生命周期内有效。

qcbq4gxm

qcbq4gxm4#

在构造函数中,你引用了一个函数;这就是被储存的东西。因为传递给构造函数的函数是一个内联函数,所以在调用ExecuteStoredLambda()时,对它的引用不再有效。要使其工作,传入一个非内联函数,或者更好的方法是将fn成员更改为对象示例而不是引用。即const std::function<void(float)> fn;(无&)

相关问题