effective c++ 条款31 避免默认的捕获模式

在c++11中,有两种默认的捕获模式:值捕获和引用捕获。默认的引用捕获可能会造成悬挂引用现象。而默认的值捕获会诱导你产生避免了这类问题想法(实际没有避免),引发你生成闭包是独立的想法(它们也可能不是)。

这就是本条款的主要结论。如果你只是工程师而非CEO,想获得更多的细节,接下来我们就开始探讨默认引用捕获带来的悬挂问题。

引用捕获使闭包持有了变量的引用,这个变量是个局部变量或者是lamda定义的作用域可见的变量。如果闭包的生命周期超过了这个局部变量或者参数的,闭包中的引用就会悬挂。举例来说,我们有一个容器,存放过滤函数,这些函数接受int类型返回bool类型。返回值表示参数是否满足函数。

using FilterContainer  =

std::vector<std::function<bool(int)>> ;

FilterContainer   filters;

我们可以增加一个被5整除过滤器:

filters.emplace_back([](int val){return val %5 == 0;});

然而除数可以在运行时确定,比如:

void  addDivisor() {

auto cal1= computeSomeValue1();

auto cal2 = computeSomeValue2();

auto divisor = computeDivisor(cal1, cal2);

filters.emplace_back([&](int val){return val %divisor == 0;});

这段代码就等着问题出现了。lamda引用到了局部变量divisor,而divisor在addDivisor返回后就不再存在了。

添加到filters的函数本质上已经是冢中枯骨了。使用这样的filter函数会导致未定义行为,从它开始创建就命中注定了。

显式的引用divisor也会出现同样的问题:

filters.emplace_back([&divisor](int val){return val %divisor == 0;});

但是通过显式捕获,可以清晰的表明lamda的生命力依赖于divisor的生命周期。同样,写出divisor的名字,提醒我们要保证这个变量的生命周期至少和lamda闭包一样长。这是个特殊的内存警示,而[&]则是个通用的内存警示“不要让任何东西悬挂”。

如果你知道闭包马上会被调用(stl的算法),而且也不会被复制,那么持有局部变量的引用时,只要确保持有的引用和引用的局部变量或参数的寿命一样长,就是无风险的。你可能会争论,不会出现悬挂引用,

从长期看,最好列出lamda用到的本地变量和参数。

一种解决方式是使用值捕获:

filters.emplace_back([=](int val){return val %divisor == 0;});

但这并不是灵丹妙药,因为可能存在指针捕获。你不能阻止lamda外指针的释放,而一旦释放,lamda内的指针就成为悬挂指针。

这不会出现,你说:我会使用智能指针,只有使用c98的loser才坚持使用原始指针,并且手工delete。这可能对的,但不相干。因为你同样使用了原始指针,而它们会在你感知之外删除。这就是现代c++的风格,可能在代码中一点痕迹没有。

class Widget {

 public:

     void addDivisor() {

         filters.emplace_back([=](int val){ return val % divisor == 0;});

  }

 

  private:

     int divisor;

}

=捕获的是this指针,所以当对象销毁时,filters就包含了悬挂指针。

改正做法是:

 void addDivisor() {

        auto divisorCopy = divisor;

         filters.emplace_back([divisorCopy](int val){ return val % divisorCopy== 0;});

  }

另外一个值捕获的缺点是,它可能会让我们产生值捕获闭包是独立的与外界隔离的错误想法。因为lamda不仅捕获局部变量和参数,同样可以捕获静态存储数据。一些对象被定义为全局对象,或者一个命名空间静态对象以及静态成员变量。这些对象可以被用在lamda中,但它们无法被捕获。

注意事项

默认的引用捕获可能会造成悬挂引用

默认的值捕获易产生悬挂指针现象(特别是this指针悬挂),也会误导lamda是独立的。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值