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式的上下文中。