罗剑锋的C++实战笔记
罗剑锋
奇虎360技术专家,Nginx/OpenResty开源项目贡献者
立即订阅
3861 人已学习
课程目录
已完结 30 讲
0/4登录后,你可以任选4讲全文学习。
课前导读 (2讲)
开篇词 | 把C++从“神坛”上拉下来,这次咱这么学
免费
课前准备 | 搭建实验环境
概论 (5讲)
01 | 重新认识C++:生命周期和编程范式
02 | 编码阶段能做什么:秀出好的code style
03 | 预处理阶段能做什么:宏定义和条件编译
04 | 编译阶段能做什么:属性和静态断言
05 | 面向对象编程:怎样才能写出一个“好”的类?
语言特性 (5讲)
06 | auto/decltype:为什么要有自动类型推导?
07 | const/volatile/mutable:常量/变量究竟是怎么回事?
08 | smart_ptr:智能指针到底“智能”在哪里?
09 | exception:怎样才能用好异常?
10 | lambda:函数式编程带来了什么?
标准库 (4讲)
11 | 一枝独秀的字符串:C++也能处理文本?
12 | 三分天下的容器:恰当选择,事半功倍
13 | 五花八门的算法:不要再手写for循环了
14 | 十面埋伏的并发:多线程真的很难吗?
技能进阶 (4讲)
15 | 序列化:简单通用的数据交换格式有哪些?
16 | 网络通信:我不想写原生Socket
17 | 脚本语言:搭建高性能的混合系统
18 | 性能分析:找出程序的瓶颈
总结篇 (5讲)
19 | 设计模式(上):C++与设计模式有啥关系?
20 | 设计模式(下):C++是怎么应用设计模式的?
21 | 知识串讲(上):带你开发一个书店应用
22 | 知识串讲(下):带你开发一个书店应用
期末测试 | 这些C++核心知识,你都掌握了吗?
结束语 (1讲)
结束语 | 路远,未有穷期
轻松话题 (4讲)
轻松话题(一) | 4本值得一读再读的经典好书
轻松话题(二) | 给你分享我的工作百宝箱
轻松话题(三) | 提高生活质量的App
轻松话题(四) | 真正高效的生活,是张弛有度
罗剑锋的C++实战笔记
15
15
1.0x
00:00/00:00
登录|注册

10 | lambda:函数式编程带来了什么?

罗剑锋 2020-05-28
你好,我是 Chrono。
第 1 节课的时候,我就说到过“函数式编程”,但只是简单提了提,没有展开讲。
作为现代 C++ 里的五种基本编程范式之一,“函数式编程”的作用和地位正在不断上升,而且在其他语言里也非常流行,很有必要再深入研究一下。
掌握了函数式编程,你就又多了一件“趁手的兵器”,可以更好地运用标准库里的容器和算法,写出更灵活、紧凑、优雅的代码。
所以,今天我就和你聊聊函数式编程,看看它给 C++ 带来了什么。

C++ 函数的特殊性

说到“函数式编程”,那肯定就要先从函数(function)说起。
C++ 里的函数概念来源于 C,是面向过程编程范式的基本部件。但严格来说,它其实应该叫“子过程”(sub-procedure)、“子例程”(sub-routine),是命令的集合、操作步骤的抽象。
函数的目的是封装执行的细节,简化程序的复杂度,但因为它有入口参数,有返回值,形式上和数学里的函数很像,所以就被称为“函数”。
在语法层面上,C/C++ 里的函数是比较特别的。虽然有函数类型,但不存在对应类型的变量,不能直接操作,只能用指针去间接操作(即函数指针),这让函数在类型体系里显得有点“格格不入”。
函数在用法上也有一些特殊之处。在 C/C++ 里,所有的函数都是全局的,没有生存周期的概念(static、名字空间的作用很弱,只是简单限制了应用范围,避免名字冲突)。而且函数也都是平级的,不能在函数里再定义函数,也就是不允许定义嵌套函数、函数套函数
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《罗剑锋的C++实战笔记》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(30)

  • 罗剑锋 置顶
    我在GitHub的lambd.cpp里写了一小段代码,示范了function + lambda实现成员函数的方法,算是对课下作业2的一个参考,同学们可以看看。
    2020-05-29
    2
    8
  • 冻冻
    老师,用“map+lambda”的方式来替换难以维护的 if/else/switch,能举个例子吗?

    作者回复: 这个需要用到std::function,存储lambda表达式,比如
    ~~~
    map<int, function<void()>> funcs;
    funcs[1] = [](){...};
    funcs[7] = [](){...};
    funcs[42] = [](){...};

    return funcs[x]();
    ~~~

    这样,就把switch/case语句转换成了function+lambda,让map替你自动switch。

    2020-05-28
    2
    12
  • 被讨厌的勇气
    采用lambda表达式替换类的成员函数,成员变量通过 '[this]'可以捕获(相当于成员函数中的this参数),参数、返回值、函数体,lambda表达式都可以实现,所以理论上,是可以替换的。

    试了一下,报错:在类内部无法定义auto。之前老师提到过的。

    作者回复: 对的,就是这个原因。

    2020-05-28
    5
  • robonix
    老师,lambda表达式是不是没有常量引用呢?如果怕修改被捕获的变量只能用值传递,那么就有拷贝发生了?

    作者回复: 目前的lambda是这样的,不过你可以不在[]里捕获,而是在函数参数里传递常量引用。

    2020-06-09
    2
  • this_is_for_u
    个人认为lambda表达式还有个重要的用途是它可以自定义stl函数谓词规则(pred),例如自定义排序规则,而无需使用传统的仿函数那种麻烦的方法。

    作者回复: 对,lambda大大方便了算法还有并发,改变了C++的编程方式。

    2020-05-28
    5
    1
  • 完全不会C++
    老师早啊!

    作者回复: morning

    2020-05-28
    1
  • Charles
    感觉lambda和函数指针的差别好像不是太大,可能是我还没有真正理解吧,我觉得lambda的捕获功能,普通函数也可以用传引用的方式实现?然后将函数打包到别的地方,是不是也可以把普通函数的函数指针打包到别的地方?

    作者回复: lambda的一个最核心的特点是变量,是“一等公民”,可以嵌套定义,而函数不能做到,在函数里无法定义函数,所以只能用lambda来就地定义匿名函数。

    2020-07-24
  • Junho
    如果把lambda表达式看做是函数对象的话,那使用lambda表达式时,需要注意它的创建/销毁的开销吗?
    我在一个游戏工程中用到它,主要用于资源加载回调的包装,也就是每次资源加载回调,几乎都涉及lambda表达式的创建。不知这种应用场景下,是否需要做特别的优化、或别的注意事项呢?

    作者回复: lambda的设计目标就是要轻便廉价地使用,虽然它很像函数对象,但编译器会尽量去做优化,比如内联,所以这点不用担心,可能它会比函数指针还要高效。

    2020-07-15
  • 娄俊峰
    老师你好:

    如果用function包装lambda表达式,是否可以作为类的成员变量?然后再lambda表达式里面操作类的成员变量。例如

    #include <funcitional>
    typedef std::function<void(int)> Handler;

    class Test
    {
    public:
            Test(int type)
            {
    if (1 == type)
    {
    handler = [this](int t)
    {
    m_int_list.push_back(t);
    };
    }
    else
    {
    handler = [this](int t)
    {
    m_int_list.push_back(t + 1);
    };
    }
    }
            
            int insert(int t)
    {
    handler(t);
    }
    private:
        Handler handler;
    std::list<int> m_int_list;
    };

    这个例子能够运行成功,并且不会出现coredump。
    但是在实际的系统中,编写了类似的代码,结果导致coredump。每当lambda表达式访问类的成员变量时,就会coredump。

    老师,这是什么原因呢?

    最后通过函数指针的形式解决的。

    作者回复: 可以用,但我觉得写的有点问题,最好是在类定义时直接初始化function,还有尽量避免if-else分支。

    你说的coredump问题不好判断原因,因为C++关联的东西比较多。

    2020-07-13
  • Geek_197dc8
    罗老师,您好,请问如何通过gdb调试复杂的lambda表达式。比如下面这个例子:
    static ResPool<RedisConnect> pool([&]() {
                            shared_ptr<RedisConnect> redis = make_shared<RedisConnect>();

                            if (redis && redis->connect(host, port, timeout, memsz))
                            {
                                    if (redis->auth(passwd)) return redis;
                            }

                            return redis = NULL;
                    }, POOL_MAXLEN);
     我想单步进入到connect 函数去调试,在这步使用gdb命令s 直接进入到c++库代码里面了。不知道怎么进入connect函数。

    作者回复: 需要注意一下C++的语法,这里只是lambda的定义,还没有执行lambda,可以在connect函数加断点,或者在connect函数调用的行上加断点。

    gdb调试lambda和普通函数是一样的,没有什么特别。

    2020-07-10
  • 王木杉
    咨询下 lambda 的原理

    作者回复: lambda的原理有点高端了,我理解它就是一个函数对象,只不过语法形式简化了,再加上了变量捕获的特性。

    2020-07-01
  • Bluebuger
    Map + lambda是个好东西 想起了以前做 虚拟机时候指令操作码处理时候的一堆case(switch语句)

    作者回复: else/switch/case是我个人认为要极力避免的,它增加了代码的复杂度,分支太多导致逻辑不清。

    2020-06-25
  • Seven
    和其他函数编程语言联系起来了!

    作者回复: 函数式编程是一种通用的范式,当然其他语言里也有,但我觉得C++的lambda更灵活,对于捕获变量的控制更精细,当然用起来也就要多小心一些。

    2020-06-23
  • Roy Liang
    C++有没有Javascript纯函数的概念?可不可以这样理解,闭包就不是一个纯函数了,因为引用了外部的变量

    作者回复: C++的lambda没有这些严谨的定义,用法很灵活,如果不捕获外部变量,或者是值捕获,就可以认为相当于JavaScript的纯函数。

    2020-06-18
  • 海怪哥哥
    关于lambda对于外部变量的捕获。大家可以这样理解更容易,如果把lambda表达式看成一个常规的变量,那么相同作用域(比如同一个函数)内的变量跟常量对它都是可见的。

    作者回复: 这个方法不错,nice。

    2020-06-12
  • xGdl
    lambda由类的operator重载而来,最大的特征是携带私货(闭包),我一般使用闭包最多的就是将任务打包Task推送线程池或下一个流程。这一过程在没有闭包之前,实现起来有些麻烦。

    作者回复: 这个就是闭包最常用的场景之一了,把数据和逻辑打包传递。

    2020-06-04
  • 神瓶中的酒
    老师~,请问方便讲解一下function-try 和 try-catch,我是C++新人,对这个不是太理解

    作者回复: function-try就是把整个函数体用try给包起来,只是形式上与普通的try-catch有点区别,效果都是一样的。

    使用function-try可以让函数少一级缩进,代码看起来更清楚,不用它也可以在函数体里自己加try-catch。

    2020-06-02
  • silverhawk
    Java lambda 里面最重要j就是在streaming处理里面做各种map, filter的逻辑,不了解C++是不是也是这样

    作者回复: lambda可以用在很多地方,封装各种逻辑块,常用的是算法和并发,后面标准库会看到这些用法。

    2020-05-31
  • 有学识的兔子
    lambda老早听说过,但工作中很少用到。
    通过老师的这个讲解,最直接的感受是范型化和函数可以捕获外部变量(这点确实符合我们做数学题的推导过程)。另外一点是,java的匿名类是不是也借鉴了lambda的函数形式?

    作者回复: Java不是太了解了,其实lambda在学术界的历史很久,只是最近这些年才被主流语言支持。

    2020-05-31
  • Eason Tai
    我理解lambda可以炫技就很帅,减少冗余。不过,一定要适度。

    lambda 表达式的形式非常简洁,可以在很多地方代替普通函数,那它能不能代替类的成员函数呢?为什么?

    不能代替。如果是引用捕获情况,lambda在执行的时候,对象的值可能发生变化。

    作者回复: 回答的不太正确,原因是auto在类里的限制,可以参考其他同学的回答。

    2020-05-30
收起评论
30
返回
顶部