考虑以下代码:
#include <memory>
#include <iostream>
class A
{
public:
A(int data) : data_(data)
{ std::cout << "A(" << data_ << ")" << std::endl; }
~A() { std::cout << "~A()" << std::endl; }
void a() { std::cout << data_ << std::endl; }
private:
int data_;
};
class B
{
public:
B(): a_(new A(13)) { std::cout << "B()" << std::endl; }
~B() { std::cout << "~B()" << std::endl; }
std::function<void()> getf()
{
return [=]() { a_->a(); };
}
private:
std::shared_ptr<A> a_;
};
int main()
{
std::function<void()> f;
{
B b;
f = b.getf();
}
f();
return 0;
}
这里看起来我正在按值捕获a_
共享指针,但是当我在Linux(GCC 4.6.1)上运行它时,会打印出来:
A(13)
B()
~B()
~A()
0
显然,0是错误的,因为A已经被破坏了。看起来this
实际上已被捕获并用于查找this->a_
。当我将捕获列表从[=]
更改为[=,a_]
时,我的怀疑得到了确认。然后打印正确的输出,并且对象的生命周期符合预期:
A(13)
B()
~B()
13
~A()
问题:
此行为是由标准,实现定义还是未定义指定的?或者我疯了,这是完全不同的东西?
答案 0 :(得分:39)
此行为是否由标准
指定
是。捕获成员变量总是通过捕获this
来完成;它是访问成员变量的唯一方法。在成员函数的范围内a_
相当于(*this).a_
。 Lambdas也是如此。
因此,如果使用this
(隐式或显式),则必须确保在lambda实例存在时对象保持活动状态。
如果要按值捕获它,则必须明确这样做:
std::function<void()> getf()
{
auto varA = a_;
return [=]() { varA->a(); };
}
如果您需要规格报价:
lambda-expression的复合语句产生函数调用操作符的函数体(8.4),但是为了查找名称(3.4),确定它的类型和值(9.3.2)并转换id-使用(* this)(9.3.1)将非静态类成员引用到类成员访问表达式中的表达式, 复合语句在lambda表达式的上下文中被考虑。