C++11 实现初始化捕获(init capture)/移动捕获

C++11 实现初始化捕获(init capture)

初始化捕获(init capture)

初始化捕获,是C++14添加的特性,用于把对象移动到闭包。

关于Lambda和闭包,见我的另一篇文章:
Lambda和闭包(closure) 及其在C++中的实现原理

使用场景和用法

使用场景
  • 当有一个大对象,在某个的lambda中被使用,且后续不会再被使用了。为了避免拷贝开销,最好move到闭包
  • 有些只能移动的资源,如std::unique_ptr
用法
  • C++14提供了初始化捕获方式:
    auto func = [bigObject = std::move(bigObject)] {
        cout << "do something" << endl;
    };

实现了对象到闭包的转移/移动,但是在C++11中不支持这种捕获方式。

  • C++11使用std::bind:
    如果拷贝捕获,将发生一次bigObject复制。
    auto func = [bigObject]() {
        cout << "do something" << endl;
    };

只能另辟蹊径,可以这么做:

    auto func = std::bind([](BigObject& object) {
        cout << "do something" << endl;
    }, std::move(bigObject));

完整用例

  • 定义一个大对象,并打印各种构造函数:
class BigObject {
public:
    char bigObj[1024] = {0};

    BigObject() {
        // cout << "Constructor" << endl;
    }

    ~BigObject() {
        // cout << "~Constructor" << endl;
    }

    BigObject(const BigObject& a) {
        cout << "Copy Constructor" << endl;
    }

    BigObject& operator=(const BigObject& a) {
        cout << "Copy Assignment operator" << endl;
        return *this;
    }

    BigObject(BigObject&& a) noexcept {
        cout << "Move Constructor" << endl;
    }

    BigObject& operator=(BigObject&& a) noexcept {
        cout << "Move Assignment operator" << endl;
        return *this;
    }
};
  • move到闭包测试
int main() {
    auto testCase = [](auto name, auto func) {
        cout << endl << "// "<< name << endl;
        func();
    };

    testCase("bad way...", [] {
        BigObject bigObject;
        auto func = [bigObject]() {
            cout << "do something" << endl;
        };
        cout << "sizeof func:" << sizeof(func) << endl;
        func();
    });

    testCase("good way(C++14)...", []{
        BigObject bigObject;
        auto func = [bigObject = std::move(bigObject)] {
            cout << "do something" << endl;
        };
        cout << "sizeof func:" << sizeof(func) << endl;
        func();
    });

    testCase("good way(C++11)...", []{
        BigObject bigObject;
        auto func = std::bind([](BigObject& object) {
            cout << "do something" << endl;
        }, std::move(bigObject));
        cout << "sizeof func:" << sizeof(func) << endl;
        func();
    });

    return 0;
}

输出为:

// test bad way...
// bad way...
Copy Constructor
sizeof func:1024
do something

// good way(C++14)...
Move Constructor
sizeof func:1024
do something

// good way(C++11)...
Move Constructor
sizeof func:1025
do something

可见,后两次都实现了move转移

值得一提的是,以上还打印了func的大小,供大家学习思考。大致说就是:

  • lambda生成的func或者说仿函数,这个仿函数有个成员就是BigObject。根据C++的对象模型,大小就是1024。
  • 而std::bind生成的func或者说仿函数,根据bind的实现原理,它把lambda和BigObject构造了新的仿函数对象,所以大小是1025。

详细的细节请查阅std::bind相关资料,或者反编译查看,这部分也非常复杂,不过多论述,浅尝辄止吧。

总结

初始化捕获(init capture),它移动资源、它离不开std::move、它看起来、用起来都是在move资源。
所以有些人称之为移动捕获,也有道理。

Links

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值