Lambda使用时可能遇到的坑
Lambda表达式给c++编程带来了很多的便利,但是在使用Lambda时需要非常小心其中可能遇到的问题,熟悉Lambda表达式才能顺利的避开各种坑
引用捕获可能带来悬挂引用
这个问题常见于使用Lambda表达式使用引用捕获某个局部变量,而调用Lambda表达式时,局部变量已经被清理导致,捕获的引用指向被清理的内存空间产生悬挂引用,例如:
#include "string"
#include "iostream"
#include "functional"
using Func = std::function;
Func GetFunc(){
int a = 1;
return [&](){ std::cout << a << std::endl; };
}
int main(int, char*[]){
std::function func = GetFunc();
func();
return 0;
}
复制代码
在上面这段代码中,在Lambda表达式func中捕获了局部变量local的引用,局部代码块执行结束后,local的生命周期结束被清理了,在外面再调用func时就使用到了一个指向已经被清理局部变量的引用。
捕获this使用时可能this已经被销毁了
这个问题与上面类似,捕获了this,但是可能在使用Lambda时this指向的对象已经被销毁,从而产生悬挂引用。例如下面的代码:
#include "string"
#include "iostream"
#include "functional"
using Func = std::function;
class Test {
public:
Test(int num): num_(num) {};
Func GetFunc(){
return [=]() {
std::cout << num_ << std::endl;
};
}
private:
int num_;
};
Func GetFunc(){
Test test(1);
return test.GetFunc();
}
int main(int, char*[]){
GetFunc()();
return 0;
}
复制代码
在上面的例子中,因为局部的Test对象local已经被销毁了,但是Lambda表达式func仍然持有该对象的引用,产生了悬挂引用。
在捕获函数自身时会出现错误
这个错误会出现在Lambda表达式中递归的调用自己,一般出现在递归函数,或者递归的回调函数中。因为引用了自身,但是引用的lambda表达式可能在创建一段时间以后被销毁掉,导致捕获的引用悬挂。例如
#include "string"
#include "iostream"
#include "functional"
volatile bool no_optimize = true; // 防止编译器优化掉而不能发现错误
using Func = std::function;
Func GetFactorialFunc(){
Func fac = [&](int n) -> int {
return n == 0 ? 1 : n * fac(n-1);
};
if (no_optimize)
return fac;
return [] (int n) { return n; };
}
int main(int, char*[]){
std::cout << GetFactorialFunc()(10) << std::endl;
return 0;
}
复制代码
在这个例子中因为回归调用引用的Lambda表达式是局部变量在函数结束时就已经被回收了,递归调用时因为使用了指向悬挂引用导致出错。