栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

《Effective Modern C++》学习笔记 - Item 32: 使用初始化捕获将对象移动至lambda函数中

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

《Effective Modern C++》学习笔记 - Item 32: 使用初始化捕获将对象移动至lambda函数中

C++11 中 lambda 函数捕获参数的语义只有两种:按值捕获 [=] 和按引用捕获 [&],而且只能声明变量名,不支持表达式。这导致了一个问题:无法使用移动构造的参数。这对于拷贝成本高而移动成本低的对象(如STL中的大部分容器)以及只能移动构造的对象(如 std::unique_ptr 和 std::future)是很不友好的。

C++14 对此做了改进,引入了一种新的捕获机制:初始化捕获(init capture),或者叫广义 lambda 捕获(generalized lambda capture),允许我们以 [param = expr] 的形式使用右侧的表达式来初始化左侧的 lambda 函数的参数。例如:

class Widget { // some useful type
...
}
...
auto pw_var = std::make_unique();		// std::unique_ptr
												// is move-only type
// configure pw_var...
auto func = [pw_param = std::move(pw_var)]{		// pw_var is move-constructed
    return pw_param == nullptr;					// into pw_param
};
func();

实际上等号右侧可以是任意表达式,因此上例中如果不需要在创建 func 前对 pw_var 做任何操作,也可以写成:[pw_param = std::make_unique()]。

如果不能使用 C++14,使用 C++11 也有解决办法。具体来说有两种:一是使用自定义类(仿函数),二是使用 lambda + std::bind。

第一种方法:使用仿函数,其实就是C++98时代实现带状态函数的方法。尽管本例不需要用到状态,但我们还是可以借用其灵活性。以上代码等价于:

class WidgetIsNullptr {
public:
    using DataType = std::unique_ptr;
    
    IsNullptr(DataType&& ptr) : pw(std::move(ptr)) {}

    bool operator()()
    { return pw == nullptr; }

private:
    DataType pw;
};
...
auto func = WidgetIsNullptr(std::make_unique());
func();

第二种方法:使用 std::bind。std::bind 接受一个可调用(Callable)对象 f 和若干个参数 args,返回一个新的可调用对象,调用该对象等价于调用 f(args)。其中 args 既可以包含调用 bind 时立刻给出的参数,也可以包含在调用 bind 产生对象时才给出的参数,使用 std::placeholders 命名空间下的 _1, _2, ... 占位符声明。这里我们把原来的 lambda 函数作为 f,需要移动的对象作为 args 调用 std::bind。

如此看起来只是在 lambda 函数外多套了一层能够实现效果的原理是:bind 产生的对象会存储一份入参的拷贝,由于我们提供的入参是 std::move(data),所以拷贝是通过调用 vector 的移动构造函数实现的。而 lambda 函数按引用接受参数——注意,是参数不是从外部域捕获变量,而这个参数由 std::bind 绑定到了刚才的(通过移动构造的)std::bind 的入参拷贝上,所以在调用 func 时,仍和之前一样不需任何参数。

std::vector data{ 1, 2, 3, 4 };

auto func = std::bind(
    [](const std::vector& data) 
    {  },
    std::move(data));
func();

尽管在 Item 34 中会说明为何应倾向于使用 lambda 函数而不是 std::bind,这里介绍的的确是 std::bind 很方便的一种场景。当然,如果能用C++14,那么初始化捕获以及 auto 参数类型会消灭这样的场景。

总结
    使用 C++14 的初始化捕获以实现将对象移动至闭包(lambda函数)中。在 C++11 中,通过手写类(仿函数)或 std::bind 模仿上面的效果。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/768731.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号