Item32 Use Init capture to move objects into closures

   一些时候Lambda的值拷贝和引用两种捕获模式都没有办法满足你的需求,比如当你有一个可移动的对象,你希望可以将这个对象移动到闭包中,因为值拷贝的效率不高,引用捕获的话会导致生命周期的问题。很可惜C++11并没有提供将对象移动到闭包中的方法,但是C++14可以,C++14对对象移动到闭包中提供了直接支持,即使你不打算升级你的编译器,本文也适合你,因为本文会介绍在C++11中通过另外一种方式间接的对对象移动提供支持。

   C++14支持将对象移动到闭包中,你可能会以为这和之前的值拷贝方式(=),引用方式(&)的使用类似,提供一个&&符号就表明是移动捕获,但是这次C++标准委员会没有这样做,他们使用了一种更灵活的方式来支持移动捕获。他们通过提供一种叫做Init capture的机制,而移动捕获就是通过这个机制来实现,在Item31中提到的C++14捕获类的数据成员的方式就是利用Init capture这种机制。下面来看看Init capture机制的语法。

  • 类的成员名称(也就是lambda生成闭包类后,要给闭包类添加的数据成员)
  • 一个表达式,用来初始化闭包类的成员

下面是一个使用Init capture进行移动捕获的例子:

class Widget {
  public:
   ...
   bool isValdated() const;
   bool isProcessed() const;
   bool isArchived() const;
  private:
   ...
};

auto pw = std::make_unique<Widget>();
// 这里的pw就是闭包类中的数据成员名称,std::move(pw) 则是一个表达式
// 用来初始化闭包类的数据成员pw
auto func = [pw = std::move(pw)] {
  return pw->isValidated()
        && pw->isArchived();
};

   C++14的这个Init capture的机制是如此强大,在没有语言层面支持的情况下C++11该如何实现C++14的这种Init capture机制呢?

  • 将要捕获的对象移动到一个std::bind产生的函数对象中
  • lambda的参数包含一个对要捕获对象的引用
std::vector<double> data;
auto func = std::bind([](const std::vector<double>& data) {
  ....
}, std::move(data));

   我们都知道std::bind默认会将参数拷贝到它产生的函数对象中保存,但是前提是参数是左值,如果是右值的话则是移动到函数对象中,使用一个左值来保存移动进去的参数。待函数对象被调用的时候将这个左值传递进去,因此这里你可以看到函数对象的形参是一个引用,这就是为什么这里不是右值引用的原因。至于形参为什么是const,这是因为默认的std::bind产生的函数对象,也称为仿函数,就是通过operator()重载来实现普通函数的行为,默认这个重载方法是const的,所以它是不能对对象成员做修改的,所以这里加上了const,如果希望可以对传入的参数进行修改,可以利用mutable关键字,如下:

std::vector<double> data;
auto func = std::bind([](std::vector<double>& data) mutable {
  ....
}, std::move(data));

总结一下本文,简单的列了一下三条要点,通过梳理可以更清晰的对本文有所理解,如下 :

  • 在C++11中是没有办法将一个对象移动到闭包中,但是却可以将一个可移动的对象移动到C++11中的bind对象
  • C++11模拟移动捕获是通过将一个右值对象移动到bind对象中,然后 通过函数对象的形参对这个移动进来的对象进行引用
  • 因为移动进来的对象和bind对象的生命周期是一样的,所以不需要担心悬挂引用的问题
展开阅读全文
©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值