c++匿名函数 原理 以及 注意点
入门介绍
按照发展历史,c++标准有如下:
- c++ 98:这个就是我们大学时候经常入门学的,比起c语言,就多了类,多态等特性
- c++ 11:加入了匿名函数了,也叫做lambda表达式
- c++ 14
- c++ 17
- c++ 20
- c++ 2b
这里重点来研究一下匿名函数。
作为直观认识,常用的介绍如下:来自 C++中Lambda表达式(匿名函数)与传统的函数指针有什么本质区别? - 知乎
可以好好看看这篇文章,写得很好。
基本语法
先看下lambda的基本语法,如下:
[capture](parameters) specifiers exception attr -> return type { /*code; */ }
- [capture]代表捕获列表,括号内为外部变量的传递方式,包括值传递、引用传递等
- (parameters)代表参数列表,其中括号内为形参,和普通函数的形参一样
- specifiers exception attr代表附加说明符,一般为mutable、noexcept等
- ->return type代表lambda函数的返回类型如 -> int、-> string等。在大多数情况下不需要,因为编译器可以推导类型
- {}内为函数主体,和普通函数一样
例子
// 1. 最简单的lambda,没有任何行为操作:
[]{};// 2. 包含两个参数的lambda:
[](float f, int a) { return a * f; };
[](int a, int b) { return a < b; };// 3. 有返回值的lambda:
[](MyClass t) -> int { auto a = t.compute(); print(a); return a; };// 4. 存在附加说明符的lambda:
[x](int a, int b) mutable { ++x; return a < b; };
[](float param) noexcept { return param*param; };
[x](int a, int b) mutable noexcept { ++x; return a < b; };// 5. 参数列表可选:
[x] { std::cout << x; }; // 去掉()
[x] mutable { ++x; }; // 编译失败!
[x]() mutable { ++x; }; // 正常编译,这是因为在附加说明符前面需要有()
[] noexcept { }; // 编译失败!
[]() noexcept { }; // 正常编译,这是因为在附加说明符前面需要有()
原理
匿名函数,是一个对象,对象,对象,重要的事情说三遍。
匿名函数定义时候,就是一个类的定义过程,然后马上会创建出一个栈对象来(不管我们有没有实际调用到这个匿名函数,当然编译器开启了优化就另说了),所以如果定义这个匿名函数的作用域中没有使用它,那么出了这个作用域,就无法访问了(当然本文后面说了可以把该对象给传递出去,就可以别的地方用了)。看如下c++示例代码:
#include <stdio.h>int main() {int x = 5;auto fun = [x]() { printf("%d\n", x); };x=6;return 0;
}
编译器处理后得到的代码:(用这个在线网站可以得到 C++ Insights,这个还可以看得到的汇编代码)
#include <stdio.h>int main()
{int x = 5;class __lambda_5_14{public: inline /*constexpr */ void operator()() const{printf("%d\n", x);}private: int x;public:__lambda_5_14(int & _x): x{_x}{}};__lambda_5_14 fun = __lambda_5_14{x}; //这儿创建出来一个栈对象x = 6;return 0;
}
- 说明,值传递捕获的变量,和内部的变量不是同一个变量了,所以匿名函数之后再改变这个外部变量的值,对匿名函数内部的值没有影响。当然,如果用引用传递捕获的,就是同一个变量了。这个文章也不错,也有讲 C++ 闭包和匿名函数 - 知乎
- 而且执行函数加了个const,说明无法对变量进行修改(也就是只读的,虽然这个变量此时是内部的拷贝变量了),但是我们可以加一个mutable实现去掉这个const
- 内部的是通过重载括号()符号,实现的函数调用的外表。该匿名函数的调用,就是增加了一行代码 fun.operator()();
- 如果匿名函数作为回调函数,传给别的函数,其实就是把该匿名函数的对象拷贝给对方函数形参,这里的对方函数的形参,一定需要是模板类,否则编译报错。这种方式,就使得这个匿名函数可以在别的作用域中使用了。所以这个匿名函数的执行功能,跟当前所在类以及所在函数的生命周期,并没有什么关系
template<typename T> void onStarted(T f)
{
f();
}
auto fun = [x]() { printf("%d\n", x); };
onStarted(fun);对应的编译器处理后的代码:
template<>
void onStarted<__lambda_12_14>(__lambda_12_14 f)
{
f.operator()();
}onStarted(__lambda_12_14(fun)); //这里的fun就是上面截图右侧的fun对象
- 注意:如果匿名函数的捕获列表[]中为空(就变成一个准函数性质了(但仍然是一个对象),否则就是准对象性质,我还没有深究,但是记住这个原则就行了,知道的也可以在评论区告诉我),那么该匿名函数就可以赋值给一个函数指针,从而当做普通函数使用。可以参考 为什么Lambda表达式分为“准对象”与“准函数”两类? - 知乎
auto f = [](int x)->int { return x; };
int (*p)(int) = f;
注意点
匿名函数不能直接捕获当前所在类的成员变量,否则编译会报错(error: capture of non-variable),需要[]中传入this指针(这个必须显示的传入),然后这个匿名函数里就可以使用this的成员啦,此时的this可以省略。
再举个例子:(有了匿名函数,可以直接拿走局部变量的值(我们只需要传递的值就行),特别方便。但是如果不用匿名函数,那么得先搞两个成员变量,把这两局部变量的值给存起来,然后在自己搞一个槽函数来读取这两个变量,其实代码量会增加很多的,逻辑搞复杂了)但是要注意:我们拿到值传递没问题,如果是拿到指针,就要特别注意了,因为出了该函数,局部变量是会被销毁的,也就是拿到的那个指针已经没有意义了,此时访问指针指向的内容,软件可能就会崩掉。
QTimer::singleShot(20, [this, path, line]() //匿名函数是真的好用,方便啊
{//访问类成员pMainWindow->gotoEditorPathAndLine(path,line);
});
参考其它博客:
c++匿名函数_shenkaibo的博客-CSDN博客_c++ 匿名函数 返回值
c++ lambda 捕获成员变量_vold1101的博客-CSDN博客_lambda表达式捕获成员变量
C++中lambda表达式中捕获的值变量存在哪? - 知乎
c++匿名函数 原理 以及 注意点相关推荐
- Python day10 global关键字、函数递归、匿名函数、map函数的用法详解
1.global关键字 引用全局变量,在局部全局变量改变,也会改变,global相当于指针,将地址指向全局变量的name name='littlepage'def littepage():global ...
- C++11:Lambda表达式(匿名函数)理解
C++在C11标准中引入了匿名函数,即没有名字的临时函数,又称之为lambda表达式.lambda表达式 实质上是创建一个匿名函数/对象.即你可以理解为(Lambda 表达式实际上是一个函数,只是它没 ...
- 4.04Day14递推、三元表达式、列表/字典推导式、匿名函数
复习:1.迭代器:不需要索引的取值方式2.生成器:通过特殊的语法自定义的迭代器3.枚举对象:为可迭代对象创建索引(按照迭代的顺序创建索引) 迭代器对象:装有多个值的容器 1. 可用通过.__next_ ...
- 《Python数据科学指南》——1.16 使用lambda创造匿名函数
本节书摘来自异步社区<Python数据科学指南>一书中的第1章,第1.16节,作者[印度] Gopi Subramanian ,方延风 刘丹 译,更多章节内容可以访问云栖社区"异 ...
- 点击事件调用匿名函数如何传参_事件发布/订阅模式的简单实现
这是一种广泛应用于异步编程的模式,是回调函数的事件化,常常用来解耦业务逻辑.事件的发布者无需关注订阅的侦听器如何实现业务逻辑,甚至不用关注有多少个侦听器存在.数据通过消息的方式可以灵活的传递. --& ...
- PHP学习笔记(一):理解匿名函数与Closure
1.PHP里的匿名函数实质是Closure类的实例 (1)不能自己实例化Closure类型的对象,会触发一个Error try{$closure = new \Closure(); }catch(Er ...
- Python 函数的执行流程-函数递归-匿名函数-生成器
1 函数的执行流程 函数的执行需要对函数进行压栈的,什么是压栈呢,简而言之就是在函数执行时在栈中创建栈帧存放需要变量以及指针的意思.具体涉及的知识非常多,这里就已一个Python脚本简单进行分析. 当 ...
- day13-递归函数、匿名函数、内置函数
一 什么是函数递归 函数递归调用(是一种特殊的嵌套调用):在调用一个函数的过程中,又直接或间接地调用了该函数本身(也叫递归调用) 1 #直接调用本身 2 def f1(): 3 print('from ...
- js 匿名函数_编写高质量箭头函数的5个最佳做法
作者:Dmitri Pavlutin译者:前端小智 来源:dmitripavlutin.com 箭头功能值得流行.它的语法简洁明了,使用词法绑定绑定 this,它非常适合作为回调.在本文中,通过了解决 ...
最新文章
- MVC分页控件之二,为IQueryable定义一个扩展方法,直接反回PagedListT结果集
- php任何读取外键数据,在表中设置外键实现的是哪一类数据完整性
- 进入docker容器之后,找不到rosbag命令(ros已经安装)
- nyoj_218_Dinner_201312021434
- MongoDB 新功能介绍-Change Streams
- C--计算求1+2!+3!+.......+n!
- Shiro总结和常见面试题
- 贝叶斯决策(Bayesian Decision Theory)
- 如何通过Homography矩阵制作虚拟图像
- 浅谈localhost
- Java中强、软、弱、虚引用
- Oracle字符串操作[转:http://www.cnblogs.com/xd502djj/archive/2010/08/11/1797577.html]
- SecureCRT远程连接虚拟机
- xbox无线适配器驱动_雷蛇推出两款适用于新 Xbox 的游戏耳机:Kaira、Kaira Pro
- MySQL高级---04
- 增值税怎么用计算机算,什么是汽车税费计算器?我们如何通过网上使用这种计算器呢?...
- Yapi远程命令执行漏洞
- WinForm常用控件
- 集合之扑克牌---洗牌+发牌+发牌后排序+看牌
- 7-8 输出上三角队形,数值从大到小
热门文章
- jplayer指定html5,jplayer列表播放错误自动进入下一首方法
- OrangePi Zero Play 72
- JavaScript中的钩子(钩子机制\钩子函数\hook)是什么?
- xtrabackup备份(全备、增量备)与恢复
- 面试不说点分布式的东西,面试官都有点看不起我呀
- php推送mip示例,WordPress 神马搜索 MIP 数据提交代码教程
- 输入一个2×3的矩阵和一个3×4的矩阵,算出两个矩阵的乘积
- 微信公众号菜单添加网址
- 北京科技大学计算机学硕难度,大家好,请问北京科技大学的电子信息专业怎么样?考研的话相对来说难吗?进复试的话差不多得多少分...
- MySQL启动不了解决方法