C ++ 11 lambda:成员variables捕获gotcha
考虑这个代码:
#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()
问题是:
这种行为是由标准的,实现定义的还是未定义的? 或者我疯了,这是完全不同的东西?
这种行为是由标准规定的
是。 捕获成员variables总是通过捕获来完成; 这是访问成员variables的唯一方法。 在成员函数的范围内, a_
相当于(*this).a_
。 兰姆达斯也是如此。
因此,如果你使用this
(隐式或显式),那么你必须确保在lambda实例处于附近时该对象保持活动状态。
如果你想通过价值捕获它,你必须明确地这样做:
std::function<void()> getf() { auto varA = a_; return [=]() { varA->a(); }; }
如果你需要一个规格报价:
lambdaexpression式的复合语句产生函数调用操作符的函数体(8.4),但为了名称查找(3.4),确定这个(9.3.2)的types和值以及转换引用使用(* this)(9.3.1)将非静态类成员转换为类成员访问expression式,复合语句被认为是在lambdaexpression式的上下文中。