(我已经读过 C++ 中 lambda 派生的隐式函子的生命周期是多少? 已经读过,它没有回答这个问题。)
我知道 C++ lambda 语法只是用于创建带有调用运算符和某个状态的匿名类实例的糖,并且我了解该状态的生命周期要求(取决于您是否通过引用的值捕获。)但是什么是 lambda 对象本身的生命周期吗?在下面的示例中,返回的
std::function
实例有用吗?
std::function<int(int)> meta_add(int x) {
auto add = [x](int y) { return x + y; };
return add;
}
如果是,它是如何工作的?这对我来说似乎有点太神奇了 - 我只能想象它通过复制我的整个实例来工作,这可能会非常重,具体取决于我捕获的内容 - 过去我主要使用
std::function
和裸函数指针,复制它们很快。鉴于 std::function
的类型擦除,这似乎也有问题。std::function
该对象将被创建,位于
struct lambda {
lambda(int x) : x(x) { }
int operator ()(int y) { return x + y; }
private:
int x;
};
std::function<int(int)> meta_add(int x) {
lambda add(x);
return add;
}
函数的本地,然后将[整个对象,包括
meta_add
的值]移动到返回值中,然后本地实例将超出范围并正常销毁。但从函数返回的对象将保持有效,只要持有它的 x
对象有效。这显然取决于调用上下文。std::function
比 lambda 更困惑。
std::function
使用一种称为类型擦除的技术。这是一个快速飞行。
std::function
为什么要删除该类型?我们想要的类型不就是
class Base
{
virtual ~Base() {}
virtual int call( float ) =0;
};
template< typename T>
class Eraser : public Base
{
public:
Eraser( T t ) : m_t(t) { }
virtual int call( float f ) override { return m_t(f); }
private:
T m_t;
};
class Erased
{
public:
template<typename T>
Erased( T t ) : m_erased( new Eraser<T>(t) ) { }
int do_call( float f )
{
return m_erased->call( f );
}
private:
Base* m_erased;
};
吗?
类型擦除允许的是
int (*)(float)
现在可以存储任何可调用的值,例如
Erased
。int(float)
int boring( float f);
short interesting( double d );
struct Powerful
{
int operator() ( float );
};
Erased e_boring( &boring );
Erased e_interesting( &interesting );
Erased e_powerful( Powerful() );
Erased e_useful( []( float f ) { return 42; } );
相当于(或可以认为是):
[x](int y) { return x + y; };
所以你的对象正在返回一个看起来像那样的对象。其中有一个定义良好的复制构造函数。所以它可以正确地从函数中复制出来似乎非常合理。
struct MyLambda
{
MyLambda(int x): x(x) {}
int operator()(int y) const { return x + y; }
private:
int x;
};
函数返回的
std::function<int(int)> meta_add(int x) {
auto add = [x](int y) { return x + y; };
return add;
}
对象实际上保存了已分配给局部变量
std::function<int(int)>
的 lambda 函数对象的移动实例。当您定义按值或按引用捕获的 C++11 lambda 时,C++ 编译器会自动生成唯一的函数类型,当调用 lambda 或将其分配给变量时会构造该函数类型的实例。为了说明这一点,您的 C++ 编译器可能会为
add
定义的 lambda 生成以下类类型:
[x](int y) { return x + y; }
那么,
class __lambda_373s27a
{
int x;
public:
__lambda_373s27a(int x_)
: x(x_)
{
}
int operator()(int y) const {
return x + y;
}
};
函数本质上等价于:
meta_add
编辑: