使用初始化捕获来把对象移动到闭包

来源:互联网 发布:linux安装jdk1.7.0 79 编辑:程序博客网 时间:2024/06/09 23:32

有时候,你想要的既不是值捕获,也不是引用捕获。如果你想要把一个只可移动对象(例如,std::unique_ptrstd::future类型对象)放入闭包中,C++11没有办法做这事。如果你有个对象的拷贝操作昂贵,但移动操作廉价(例如,大部分的标准容器),然后你需要把这个对象放入闭包中,那么比起拷贝这个对象你更愿意移动它。但是,C++11还是没有办法完成这事。

但那是C++11,C++14就不一样啦,它直接支持将对象移动到闭包。如果你的编译器支持C++14,欢呼吧,然后继续读下去。如果你依然使用C++11的编译器,你还是应该欢呼和继续读下去,因为C++11有接近移动捕获行为的办法。

缺少移动捕获被认为是C++11的一个缺陷,最直接的补救方法是在C++14中加上它,但标准委员会采用了另外一种方法。它们提出了一种新的、十分灵活的捕获技术,引用捕获只是属于这种技术的其中一种把戏。这种新能力被称为初始化捕获(init capture),实际上,它可以做C++11捕获格式能做的所有事情,而且更多。初始化捕获不能表示的是默认捕获模式,不过条款31解释过无论如何你都应该远离默认捕获模式。(对于将C++11捕获转换为初始化捕获的情况,初始化捕获的语法会比较啰嗦,所以如果C++11捕获能解决问题的情况下,最好使用C++11捕获。)

使用初始化捕获让你有可能指定

成员变量的名字(留意,这是闭包类的成员变量,这个闭包类由lambda生成)和 (初始化那成员变量的)表达式 。

这里是如何使用初始化捕获来把std::unique_ptr移动到闭包内:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
classWidget {
public:
    ...
    bool isValidated() const;
    bool isProcessed() const;
    bool isArchived() const;
private:
    ...
};
 
auto pw = std::make_unique<widget>();  //创建Widget
 
...             // 配置*pw
 
auto func = [pw = std::move(pw)]  // 以std::move(pw)来初始化闭包中成员变量pw
            {returnpw->isValidated() && pw->isArchived(); }  </widget>

初始化捕获的代码部分是pw = std::move(pw),“=”左边的是你指定的闭包类的成员变量名,右边的是进行初始化表达式。有趣的是,“=”左边的作用域和右边的作用域不同,左边的作用域是在闭包类内,而右边的作用域和lambda被定义的地方的作用域相同。在上面的例子中,“=”左边的名字pw指的是闭包类的成员变量,而右边的名字pw指的是在lambda之前声明的对象,即由make_unique创建的对象。所以pw = std::move(pw)的意思是:在闭包中创建一个成员变量pw,然后用——对局部变量pw使用std::move的——结果初始化那个成员变量。

通常,lambda体内代码的作用域在闭包类内,所以代码中的pw指的是闭包类的成员变量。

在上面例子中,注释“配置*pw”表明了在std::make_unique创建Widget之后,在lambda捕获指向Widget的std::unique_ptr之前,Widget在某些方面会被修改。如果这个配置不是必需的,即,如果std::make_unique创建的Widget对象的状态已经适合被lambda捕获,那么局部变量pw是不必要的,因为闭包类的成员变量可以直接被std::make_unique初始化:

?
1
2
auto func = [pw = std::make_unique<widget>()]        // 以调用make_unique的结果
{returnpw->isValidated() && pw->isArchived(); }; // 来初始化闭包的局部变量pw

这应该清楚地表明在C++14中,C++11的“捕获”概念得到显著推广,因为在C++11,不可能捕获一个表达式的结果。因此,初始化捕获的另一个名字是generalized lambda capture(广义lambda捕获?)。

阅读全文
0 0