c++ lambda

lambda


1. 仿函数(函数对象)、闭包closure、closure对象

  • 仿函数functors

仿函数,也称函数对象function object,类中重载operator()操作符,使得类的使用看上去像一个函数,通常用于STL算法中的最后一个自定义参数。

// 仿函数
class Test {
public:
    Test(int value) : value_(value) {};
    bool operator() (int x)
    {
        return x > value_;
    }
private:
    int value_;
};
  • 闭包closure、闭包对象

闭包可以认为是一组变量和函数组成的类。 lambda实质上可以看成一个匿名函数,而lambda对外部变量的捕获后称为closure,lambda表达式的底层实现称为closure对象。 lambda按值捕获会给人一种闭包自足的情况,但是按值捕获指针如this的时候也需要关注this的生命周期,按值捕获时lambda使用static变量时也和外界参数有关,不能说值捕获就是不依赖外界的闭包自足情况。


2. lambda语法

[捕获列表] (参数列表) -> 返回类型 {函数体}

  • 捕获列表: 实现闭包功能,可以捕获外部变量供lambda使用
    • 捕获方式:默认值捕获,显示值捕获为[=value], 引用捕获为[&value], 捕获多个变量且捕获方式不同 [=value1, &value2], 全部值捕获[=], 全部引用捕获[&]
    • 注意1:按引用捕获时需要注意引用的变量生命周期和lambda被使用的生命周期
    • 注意2:lambda捕获的对象只能是lambda作用域内的可见的非静态局部变量,包括参数,特别注意在类成员函数中的lambda不能直接捕获类的成员变量,因为有隐式的this捕获存在,如果有捕获变量声明周期危险,可以定义新的局部变量 = 类成员变量,再捕获本地新局部变量
    • 注意3:c++11不支持移动捕获(初始化捕获),c++14可以在捕获列表中使用[value = std::move(outValue)]的方式实现移动捕获。如果不支持c++14,可以用老方法,仿函数(函数对象)来实现移动捕获,在构造对象的时候将数据移动给类变量;也可以使用bind方式,将数据移动至bind对象中,再通过引用传递给lambda。
    • 注意4:lambda生成的闭包类中operator()函数是const的,通过bind引用变量如果是非const的,需要去掉lambda的const属性,通过 []() mutable {}实现
// c++11
std::vector<double> data;
auto func = std::bind([](std::vector<double> &data) mutable {

}, std::move(data));
// 等同于c++14 初始化捕获
auto func = [data = std::move(data)]() {

};

// c++11
auto func2 = std::bind([](const TSome &data) {

}, make_unique<TSome>());
// 等同于c++14 初始化捕获
auto func2 = [data = make_unique<TSome>()]() {

};
  • 参数列表:和普通函数列表一致
    • c++14支持auto泛型参数,相当于仿函数operator函数为模板函数. 完美转发实现万能引用时,转发模板T由于auto无法推到T是啥,故使用decltype(param)得出,decltype(param) 如果param是左值引用则返回左值引用,如果param是右值引用则返回右值引用。
// c++14 
auto f = [](auto &&x){ 
    return func(normalize(std::forward<decltype(param)>(x))); 
};

// c++14 变参
auto func = [] (auto&& ...params) {
    return func(normalize(std::forward<decltype(params)>(params)...));
}

// 仿函数不用auto,直接可以std::forward<T>(x)
class Test {
public:
    template<typename T>
    auto operator(T&& x) const
    {
        return func(normalize(std::forward<T>(x)));
    }
};

  • 返回类型:和普通返回类型一致,可以省略->返回类型,自动推到返回类型
  • 函数体:可以操作参数列表和捕获列表中的变量

3. 例子:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

// 仿函数
class Test {
public:
    Test(int value) : value_(value) {};
    bool operator() (int x)
    {
        return x > value_;
    }
private:
    int value_;
};

int main()
{
    vector<int> v{1, 32, 4512, 5, 32, 44};
    int value = 4;

    // 没有lambda的时候, 仿函数/函数对象实现
    cout << count_if(v.begin(), v.end(), Test(4)) << endl; // 5

    // lambda 
    cout << count_if(v.begin(), v.end(), [value](int x) {
        return x > value;
    }) << endl;  // 5

    // 引用捕获
    auto func = [&value](int x){ return x > value;};
    cout << count_if(v.begin(), v.end(), func) << endl; // 5
    value = 32; 
    cout << count_if(v.begin(), v.end(), func) << endl; // 2
    return 0;
}

备注

  • closure: 闭包可以理解成"定义在一个函数内部的函数"。在本质上,闭包是将函数内部和函数外部连接起来的桥梁,闭包技术使得内部函数可以使用外部函数的局部变量。
  • 仿函数:实质是类重载operator()后的结果,使用过程中看起来像函数调用,有自己的类成员变量。
  • std::fucntion:通用函数包装器,可以存储、赋值、调用任何可调用的目标:包括函数、lambda、bind表达式、成员函数等。
  • bind:参数绑定,c++11前用的比较多,c++11大部分场景用lambda代替,只有在移动捕获和模板参数时先用bind参数后再传给lambda,c++14后lambda支持auto后bind几乎不需要使用。
  • lambda:便捷匿名函数,易用的参数绑定。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值