(我已经阅读了What is the lifetime of lambda-derived implicit functors in C++?,但它没有回答这个问题。)
我理解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
的类型擦除,它似乎也存在问题。
答案 0 :(得分:57)
如果你用手动编织器替换你的lambda,那么它的生命周期正是如此:
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
对象发生,该函数返回的对象将保持有效。这多长时间显然取决于调用上下文。
答案 1 :(得分:16)
对于std::function
而言,你似乎比lambdas更加困惑。
std::function
使用了一种名为type-erasure的技术。这是一个快速的飞行。
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; } );
答案 2 :(得分:10)
这是:
[x](int y) { return x + y; };
相当于:(或者也可以考虑)
struct MyLambda
{
MyLambda(int x): x(x) {}
int operator()(int y) const { return x + y; }
private:
int x;
};
所以你的对象正在返回一个看起来就像那样的对象。其中有一个定义良好的复制构造函数。因此,它可以从函数中正确复制似乎非常合理。
答案 3 :(得分:4)
在您发布的代码中:
std::function<int(int)> meta_add(int x) {
auto add = [x](int y) { return x + y; };
return add;
}
函数返回的std::function<int(int)>
对象实际上包含已分配给局部变量add
的lambda函数对象的移动实例。
当您定义捕获按值或按引用的C ++ 11 lambda时,C ++编译器会自动生成唯一的函数类型,其实例是在调用lambda或将其赋值给变量时构造的。为了说明,您的C ++编译器可能会为[x](int y) { return x + y; }
定义的lambda生成以下类类型:
class __lambda_373s27a
{
int x;
public:
__lambda_373s27a(int x_)
: x(x_)
{
}
int operator()(int y) const {
return x + y;
}
};
然后,meta_add
函数基本上等同于:
std::function<int(int)> meta_add(int x) {
__lambda_373s27a add = __lambda_373s27a(x);
return add;
}
编辑:顺便说一句,我不确定你是否知道这一点,但这是C ++ 11中函数currying的一个例子。