C++11 lambda函数

2023-10-30

用过脚本语言的童鞋都知道,函数的定义可以在任何地方,例如:

local function table_sort(t)
    local function sort_by_len(t1, t2)
        return #t1 < #t2
    end
    
    table.sort(t, sort_by_len)
    
    
    --上面的代码也可以简写为:
    --table.sort(t, function(t1, t2)
    --    return #t1 < # t2
    --end)
end

我们应该提倡这种内部函数的用法,他能够让代码更加简洁,而且耦合度更低,因为他在外部是不可见的,我们不需要或不允许被外部可见。遗憾的是在C++11之前没有所谓的这种内部函数,从C++11开始支持内部函数,他有个学名lambda函数,或者lambda表达式。以下是C++11 一个lambda函数的一个例子:

void main()
{
    auto val_lam = [](int x) {return x * x + 1; };
    printf("a is %d\n", val_lam(1));
}

从上面的代码看出,lambda函数比较实用,也比较简单。一般我们使用lambda函数实现一些简短独立的功能,因此lambda函数一般是孤立存在的,也就是说不会访问外部变量。但是并没有规定他不允许访问外部变量,所以lambda函数需要处理变量与外部变量的关系。说的直白一点就是,如果在lambda函数中改变了一个外部变量,那么这个外部变量真的会改变吗?例如将上面的代码变为:

void main()
{
    int x = 10;
    auto val_lam = []() {x++; return x + 1; };
    printf("a is %d\n", val_lam());    //lambda函数的值是多少,现在x是多少?
}

请问val_lam()的返回值是多少,i又是多少呢?答案是程序根本就编译不过。为什么会这样子?为了安全起见,lambda函数引用外部变量时,必须先在[]中捕获外部变量,也就是这样:

void main()
{
    int x = 3;
    int y = 5;
    auto val_lam = [x,y]() {x++; return x + y; };
    printf("a is %d\n", val_lam());    //lambda函数的值是多少,现在x是多少?
}

编译还是报错!提示说x,y是只读变量。还是安全原因,C++11默认把lambda函数设定为const函数。要想改变外部函数的值,必须去掉const属性,对C++比较了解的应该知道,加上关键字mutable就可以了,即:

auto val_lam = [x,y]() mutable {x++; return x + y; };

我们把上面引用外部变量的方式叫做值捕获,即不会改变外部变量的值,只是传递值的作用。其实lambda函数中的[x,y]可以简写为[=],编译器会自动推导出相关的变量x,y。

值捕获的坑

关于值捕获要注意的地方是,与参数传值类似,值捕获的前提是变量可以拷贝,不同之处则在于,被捕获的变量在 lambda 表达式被创建时拷贝,而非调用时才拷贝,例如:

void main()
{
    int x = 10;
    auto val_lam = [=]() {return x + 1; };
    x = 20;
    printf("a is %d\n", val_lam());    //lambda函数的值为11,而不是21
}

我们看到lambda函数的值为11,而不是21。因为值捕获在创建时就传入了。

值捕获还有一个需要注意的地方,不容易理解,也是其特性,就是外部值的连续性:

void main()
{
	int x = 10;
    auto val_lam = [=]() mutable {x++; return x + 1; };

    printf("a is %d\n", val_lam());     //12 
	printf("a is %d\n", val_lam());     //13
	printf("a is %d\n", val_lam());     //14
	printf("x is %d\n", x);             //此时x还是10
}

看到没,多次调用lambda函数,发现其记住了外部变量的值。几年前学习到这个特性的时候,也是很不能理解,后来用多了lua的闭包函数和upvalue,我现在可以会心的一笑了。事实上利用这个特性可以实现高阶函数,这个我在lua里用的比较多。

引用捕获

与值捕获对应的就是引用捕获,引用捕获可以真正改变外部的值,因为引用捕获就是引用的外部变量地址。

void main()
{
	int x = 10;
    auto val_lam = [&]() mutable {x++; return x + 1; };

    printf("a is %d\n", val_lam());     //12 
	printf("a is %d\n", val_lam());     //13
	printf("a is %d\n", val_lam());     //14
	printf("x is %d\n", x);             //此时x已经变为13
}

我们看到引用捕获其实和函数参数传引用类似,作用也是一致的,一方面可以改变变量的值,另一方面在面向对象编程时,引用捕获相比值捕获可以省去新对象的构造,节省开销。

完整定义

我们一步步介绍了lambda函数的用法,实际上我们还没有对lambda函数的完整定义做介绍。事实上这也是我自己写文章和学习的风格,如果还没有接触一个概念之前,就完整介绍概念的定义,用法,那就是典型的教科书式写法。这是我很反对的,因为学习是一个循序渐进的过程,最好的办法就是用最少最简单的,已知的知识方式介绍一个概念,否则一上来就介绍大段的定义和理论,是会吓坏新手的。

好了,我们看看lambda函数的完整定义:

1、捕获子句(lambda表达式从此处开始,也叫做lambda-introducer。)

2、参数列表(可选),也叫做lambda声明符(lambda declarator)。

3、mutable 选项(可选),加上mutable后,lambda表达式体内的语句可以修改按值捕获的变量。

4、异常选项(可选),加上throw()表示lambda表达式不抛出任何异常(别搞反了)。

5、尾部返回类型(trailing-return-type) (可选),一般来讲lambda表达式的返回值都可以由编译器自动猜测除非你指明了尾部返回类型。

6、lambda 本体,也就是函数体部分。

以上有些定义是我们不常见的,等遇到时再去查看具体用法,这样有针对性的学习才是有效率的。

 

欢迎加入QQ群 858791125 讨论skynet,游戏后台开发,lua脚本语言等问题。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++11 lambda函数 的相关文章

随机推荐

  • 二叉树学习笔记之B树、B+树、B*树

    动态查找树主要有二叉查找树 Binary Search Tree 平衡二叉查找树 Balanced Binary Search Tree 红黑树 Red Black Tree 都是典型的二叉查找树结构 查找的时间复杂度 O log2 N 与
  • Recyclerview列表item设置成等宽高的正方形,通过计算宽度动态赋值

    首先是效果图 然后是关键代码 onBindViewHolder 给Item元素赋值 Override public void onBindViewHolder ViewHolder holder int position 获取内容layou
  • hdu 1028 Ignatius and the Princess III

    Problem acm split hdu edu cn showproblem php pid 1028 Reference 母函数 Generating function 详解 TankyWoo ACM 母函数专题 Meaning 将一
  • 代码审查领域所面临的瓶颈分析

    代码审查是检查代码中存在缺陷的重要手段 通常分为两阶段进行 一个阶段在代码解析阶段进行 主要应用词法分析 语法分析以及控制流分析等技术检测同数据传递无关的代码缺陷 另外一个阶段是在代码解析后进行 主要应用数据相关路径遍历等技术检测同数据相关
  • 通过Gazebo仿真学TurtleBot3(二)——环境搭建

    1 Ubuntu安装 网络上教如何安装ubuntu的文章很多 在这里就不再花时间详细介绍了 本文使用的是ubuntu16 04 LTS版本 针对是使用物理机双系统还是虚拟机的问题 我建议资源允许的情况下可以都装 物理机的运行速度更快 尤其是
  • Windows如何修改桌面右键的菜单栏

    前言 可能很多人都会遇到这样一个情景 安装多款软件后 桌面上点击右键 发现得到的菜单栏异常的臃肿 亦或者找不到需要快捷操作的动作 所以需要根据个人需要适当的瘦身与增肌 举例 这是我目前在桌面右键点击获得的菜单栏 以删除其中PyCharm并添
  • linux:docker-compose下载后无法使用

    参考 Ubuntu 安装 Docker Docker Compose 知乎 解决方法 PC上下载二进制文件 之后filezilla上传到服务器对应目录
  • 黑客是如何炼成的?这8个网站也许可以帮到你!

    黑客攻击是一项很难掌握的技能 在很大的程度上要求人们对计算机和软件架构的各种概念和网络系统有深入的了解 今天 分享8个道德黑客学习可以利用的网站 黑客主要有两种 黑帽黑客 白帽黑客 黑帽黑客为了个人利益 利用自身的计算机系统知识侵入系统 这
  • 苹果电脑mac计算机图标怎么删除吗,苹果电脑桌面的图标怎么删除不了怎么办

    1 怎么删除苹果Mac桌面图标 桌面图标被删除了怎么恢复 一 怎么删除苹果Mac桌面图标 删除苹果Mac桌面图标方法很简单 对着图标单击鼠标右键 在出现的菜单中鼠标指向 选项 在次级菜单中点击选择 从Dock中移除 即可 二 苹果Mac桌面
  • java中获取当时程序运行时间的函数currentTimeMillis()

    以下是一个代码运行的时间 class zhishu public static void main String args int num 100 int i j flag 获取当前时间距离1970 01 01的毫秒数 long start
  • 解决vue表格列错位问题

    在切换菜单后 或者使用v if切换tab选项卡的时候 子内容表格的列可能会出现错位问题 官方给出的解决办法是使用doLayout方法 在这里总结了两种方法 方法1 使用doLayout
  • Git使用教程:最详细、最傻瓜、最浅显、真正手把手教!

    转载自 Git使用教程 预警 因为详细 所以行文有些长 新手边看边操作效果出乎你的预料 一 Git是什么 Git是目前世界上最先进的分布式版本控制系统 工作原理 流程 Workspace 工作区 Index Stage 暂存区 Reposi
  • Mac PHP代码延时更新慢解决方案

    自己试了好多都不行 最后在这找到的解决方案 亲测有效 用phpinfo 函数 查看PHP开启模块中是否包含ZendOpcache这类opcode缓存 如果有 那么打开php ini查找下 opcache 将60改为0即可立即生效 opcac
  • idea中如何生成程序运行的时序图

    IDEA中如何生成程序运行的时序图 程序运行的时序图 可以帮助我们分析程序执行流程和理解一些关键的业务逻辑或者阅读源码 那么怎么样才能生成时序图尼 经过了一番寻找 发现idea中有个插件可以实现自动生成代码的时序图 时序图生成工具 Sequ
  • Linux模拟弱网丢包、延时和限制带宽

    Linux操作系统中的流量控制器TC Traffic Control 用于Linux内核的流量控制 主要是通过在输出端口处建立一个队列来实现流量控制 通过tc命令我们可以模拟弱网进行测试 note 命令中eth0是需要模拟弱网的网卡 可通过
  • pandas1-数据的增删改查

    文章目录 核心数据结构 Series DataFrame 数据查改 对Series操作 对DataFrame操作 对单列数据访问 对多列数据访问 对某几行访问 loc与iloc ix方法 数据修改 更新修改DataFrame中的数据 为Da
  • Java中的多线程

    java中的多线程是同时执行多个线程的过程 线程基本上是一个轻量级的子进程 是一个最小的处理单元 多处理和多线程都用于实现多任务 但是我们使用多线程而不是多进程 因为线程共享一个共同的内存区域 它们不分配单独的内存区域 因此节省内存 并且线
  • 我的Java后端书架

    这位大侠 这是我的公众号 程序员江湖 分享程序员面试与技术的那些事 干货满满 关注就送 我的Java后端书架 2016年暖冬4 0版 原文出处 江南白衣 书架主要针对Java后端开发 3 0版把一些后来买的 看的书添补进来 又或删掉或降级一
  • git: Your branch and 'origin/master' have diverged解决方法

    如果不需要保留本地的修改 只要执行下面两步 git fetch origin git reset hard origin master 当我们在本地提交到远程仓库的时候 如果遇到上述问题 我们可以首先使用如下命令 git rebase or
  • C++11 lambda函数

    用过脚本语言的童鞋都知道 函数的定义可以在任何地方 例如 local function table sort t local function sort by len t1 t2 return t1 lt t2 end table sort