【C++11】来感受lambda表达式的魅力~

在这里插入图片描述

👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:C++航路
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨


一、仿函数的使用

假设需要对vector容器里的数据进行排序,那么可以通过算法库的 sort 函数进行排序,至于结果为升序还是降序,可以通过 仿函数( 类重载operator() 控制(类对象可以像函数一样使用)。

struct Great // > - 降序
{
    bool operator()(int x, int y)
    {
        return x > y;
    }
};

struct Less // < - 升序
{
    bool operator()(int x, int y)
    {
        return x < y;
    }
};

int main()
{
    vector<int> v{1, 3, 5, 9, 7, 0, 4, 2, 6, 8};

    // Less() - 仿函数的匿名对象
    sort(v.begin(), v.end(), Less()); // 升序
    cout << "升序:";
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;

    // Great() - 仿函数的匿名对象
    sort(v.begin(), v.end(), Great()); // 降序
    cout << "降序:";
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;

    return 0;
}

【程序结果】

在这里插入图片描述

这看起来仿函数用起来也非常的香啊~。但如果某些程序员非常不注重代码风格,是这样写代码的:

struct func1 // > - 降序
{
    bool operator()(int x, int y)
    {
        return x > y;
    }
};

struct func2 // < - 升序
{
    bool operator()(int x, int y)
    {
        return x < y;
    }
};

sort(v.begin(), v.end(), func1()); 
sort(v.begin(), v.end(), func2()); 

这样写是不是非常的恶心,这样命名风格就算了,旁边还不给个注释,这不是欠揍嘛hh;而且万一仿函数在别的文件里,那么我们还要去找。

因此C++11支持了一个新语法:lambda 表达式。它可以快速构建局部的匿名函数对象,作为函数中的参数

接下来大家先见见猪跑,至于怎么使用lambda 表达式,后面会介绍

int main()
{
    vector<int> v{1, 3, 5, 9, 7, 0, 4, 2, 6, 8};

    sort(v.begin(), v.end(), [](int x, int y)->bool {return x < y; }); // 升序
    cout << "升序:";
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;

    // Great() - 仿函数的匿名对象
    sort(v.begin(), v.end(), [](int x, int y)->bool {return x > y; }); // 降序
    cout << "降序:";
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;

    return 0;
}

【程序结果】

在这里插入图片描述

由此可见,lambda表达式相对于传统的仿函数来说,更加灵活和简洁

二、lambda表达式的语法

书写格式:[ ]( ) mutable ->returntype { }

  • []捕捉列表。该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数。捕捉列表能够捕捉lambda所在域中的变量供lambda函数使用

  • ()参数列表。与普通函数的参数列表一个意思。注意:如果没有参数,可以直接将()省略。

  • 关键字mutable取消捕捉列表中参数的常量属性。注意:使用该修饰符时,即使参数为空,参数列表的()不可省略。(基本不常用)

  • ->returntype返回值类型返回值类型几乎可以不写,它会根据return语句来推断。

  • {}函数体。和普通函数一样,同样支持多语句。但不能函数体内不能使用lambda所在局部资源,但可以使用全局一切资源。如果想使用局部资源,需要配合捕捉列表

这里再次声明:

  • lambda 表达式 构建出的是一个 局部的匿名函数对象,匿名函数对象也可以调用,不过要在创建后立马调用,因为匿名对象生命周期只有一行

  • 如果需要显式调用可以使用 auto 推导匿名函数对象 的类型,然后将创建出来的 匿名函数对象 赋给一个 有名函数对象

    在这里插入图片描述

三、lambda表达式中捕捉列表的玩法

需要注意的是:lambda函数体内不允许使用当前作用域中的变量,除非在捕捉列表声明,否则会报错。

在这里插入图片描述

捕捉列表可以“捕捉”哪些数据可以被lambda使用,以及是值传递还是引用传递

  • [val1,val2, ...]:表示值传递捕捉变量
  • [&val1, &val2, ...]:表示引用传递捕捉变量

比如,使用lambda表达式交换局部变量ab

在这里插入图片描述

这是因为 捕捉列表 中的参数是一个传值捕捉,而捕捉列表 中的参数默认具有 常量属性,不能直接修改,但可以添加 mutable 关键字 取消常性

在这里插入图片描述

但输出结果发现两个值并没有修改。这里其实就类似于普通函数的值传递,局部变量的ab只是拷贝给捕捉列表参数,并没有建立联系,因此,这里的捕捉列表需要用引用传递的方式

在这里插入图片描述

  • [&]表示引用传递捕捉所有在当前作用域中的所有变量

在这里插入图片描述

  • [=]表示值传递捕捉所有在当前作用域中的所有变量。

在这里插入图片描述

注意:

  • 捕捉列表 的使用非常灵活,比如 [&, x] 表示只有x 使用 传值捕捉,其他变量使用 引用捕捉[=, &x] 表示只有 x 使用 引用捕捉,其他变量使用 传值捕捉

  • 捕捉列表不允许变量重复传递,否则就会导致编译错误。比如:[=, a]:捕捉a重复。

  • lambda表达式之间不能相互赋值。因为它们之间的类型是不一样的!至于为什么呢?后面讲底层就知道了~

在这里插入图片描述

四、lambda表达式的底层原理

其实lambda表达式用起来非常香,这是因为它的底层是仿函数;就像范围for一样,看似是简短的几行代码,底层确实一个迭代器。

我们可以使用以下代码样例,通过查看反汇编:

struct func
{
    void operator()(int x, int y)
    {
        cout << "operator()(int x, int y)" << endl;
    }
};

int main()
{
    int a = 3;
    int b = 3;
    
    // 仿函数对象实例化
    func f;
    f(a, b);

    // lambda表达式
    auto e = [=](int x, int y) {
        cout << "[=](int x, int y)" << endl;
    };
    e(a, b);

    return 0;
}

【反汇编】

在这里插入图片描述

实际在底层编译器对于lambda表达式的处理方式,完全就是按照仿函数的方式处理的,即:如 果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator(),而捕捉列表就相当于成员变量

并且这个类被命名为:lambda_uuid

在这里插入图片描述

uuid通用唯一标识码,可以生成一个重复率极低的辨识信息,避免类名冲突,这也意味着即便是两个功能完全一样的 lambda 表达式,也无法进行赋值,因为 lambda_uuid 肯定不一样

所以在编译器看来,lambda 表达式 本质上就是一个 仿函数

五、总结

当涉及到C++lambda表达式时,可以得出以下总结:

  1. 匿名函数: lambda表达式允许在需要函数对象的地方快速定义匿名函数,无需显式命名,可直接内联使用。

  2. 捕获外部变量: lambda表达式能够捕获其作用域内的变量,可以按值或按引用捕获,使得在算法和回调函数中处理外部变量更加方便。

  3. 简洁性: lambda表达式使代码更加紧凑和简洁,尤其在需要传递简单的函数对象时,可以省去冗余的代码。

  4. 可读性: 使用lambda表达式可以将算法和行为直接嵌入到使用它们的地方,使代码更具可读性和易于理解。

  5. 函数式编程: 引入lambda表达式后,C++增强了函数式编程的能力,使得处理一些函数式编程的场景更加便利。

需要注意的是,lambda表达式并不是完全取代传统的仿函数,而是在某些情况下更加方便和实用。例如,在需要长期复用、需要命名或需要在多个地方多次使用的情况下,传统的仿函数仍然有其价值。

  • 30
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值