以下 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();
}
您没有存储 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
};
lambda 函数
这可能是你的理解误入歧途的地方。 Lambda 是带有成员函数的对象;它们本身并不是函数。它们的定义看起来像函数体,但这实际上是对象的调用运算符
operator()
的定义。
您对场景的评估的半修正版本:
lambda object 似乎在构造函数中正确地调用其运算符,但后来,同一个 object 不再起作用!
为什么只是“半”修正?因为在
LambdaStore
内部,您不能直接访问 lambda 对象。相反,您可以通过(引用) std::function
对象来访问它。更正确的版本:
对象似乎在构造函数内正确调用其运算符,但后来,同一个对象不再起作用!std::function
如果我去掉“lambda”的概念,也许这会更清楚?您的主要功能基本上是以下功能的语法快捷方式。
struct Functor {
void operator()(float a) {
cout << a << endl;
}
};
int main()
{
LambdaStore lambdaStore(Functor{});
lambdaStore.ExecuteStoredLambda();
}
在此版本中,应该更容易看到您创建了一个临时对象作为
LambdaStore
构造函数的参数。 (实际上,您创建了两个临时对象 - 显式 Functor
对象和隐式 std::function<void(float)>
对象。)然后您可能会注意到,您存储的引用在构造函数完成后立即变为悬空...
lambda 的寿命有限制吗?
是的,所有具有非静态(和非线程)存储持续时间的对象在 C++ 中都有有限的生命周期。
是的,临时变量(包括 lambda)的生命周期是有限的,引用并不能使其保持活动状态。您可能想存储一个副本。您的问题与您存储引用的任何其他临时变量(如
int
)相同。如果引用的变量要在引用的生命周期内有效,则它必须比引用的生命周期长。
在构造函数中,您引用了一个函数;这就是正在存储的内容。由于传递给构造函数的函数是内联函数,因此在调用
ExecuteStoredLambda()
时,对其的引用不再有效。要使其正常工作,请传入非内联函数,或者更好的是,将 fn
成员更改为对象实例而不是引用。 IE
const std::function<void(float)> fn;
(无&)