避免使用默认捕获模式
两种默认模式:引用和按值
按引用捕获会导致空悬行为,如果使用,请保证引用变量的生命周期与lambda的生命周期一致。
vector<std::function<bool(int)>> filters;
void AddDivisorFilter(){
auto divisor = GetDivisor();
filters.emplace_back([&](int num){return num % divisor == 0})//对divisor的指涉可能空悬!
}
按值捕获要注意如果捕获到指针(类的this指针)也会导致空悬行为, 捕获只能在创建lambda式的作用域内可见的非静态局部变量(包括形参)。
class widget{
public:
...
void addFilter() const;
void addFilter1() const;
private:
int divisor;
};
void widget::addFilter1(){
filters.emplace_back([](int num){return num % divisor == 0});//错误,没有可以捕获的divisor
}
void widget::addFilter(){
filters.emplace_back([=](int num){return num % divisor == 0});//这里divisor相当于 this->divisor
}
使用默认值捕获模式,不是自洽的。静态存储期对象可以在lambda内使用,但是他们不能被捕获,但如果使用了默认值捕获模式,这些对象就会给人以错觉,认为它们可以加以捕获。
void addDivisorFilter()
{
static auto divisor = GetDivisor();
filters.emplace_back([=](int num){return num % divisor == 0})//未捕获到任何东西!divisor指涉到之前static 修饰的对象
++divisor;//意外修改了divisor
}
使用初始化捕获将对象移入闭包
以下lambda表达式只能在c++14以上中使用,成为初始化捕获(广义lambda捕获)。
初始化捕获能完成c++11捕获中所有能完成的事情。
在lambda表达式[]中=左边的是闭包类的作用域,=右边的作用域与lambda式加以定义处的作用域。
class widget{
...
}
auto pw = std::make_unique<widget>();
auto func = [pw = std::move(pw)]{
return pw->isValidated()
&& pw->isArchived();};
在c++11中模拟初始化捕获的方法如下所示:
std::vector<double> data;
...
auto func = std::bind(
[](const std::vector<double>& data){
....//数据操作
},std::move(data));
绑定对象包含传递给std::bind所有实参的副本。对于每个左值实参,在绑定对象内的对应的对象内对其实施复制构造,对于每个右值实参,则进行移动构造。
对auto&& 型别的形参使用decltype,从而实现完美转发
auto f = [](auto&&... params)
{ return func(normalize(std::forward<decltype(params)>(params...)))};