浅谈C++ Lambda 表达式(简称LB)
C++ 11 对LB的支持,对于喜欢Functional Programming的人来说,无疑是超好消息。它使得C++进入了和C#,JavaScript等现代流行的程序设计语言所代表的名人堂。
不熟悉LB本身的网友,可以看MSDN文章
( http://msdn.microsoft.com/en-us/library/dd293608.aspx ),我仅仅简单地分析一下VC++中LB的用法,实现,和性能。
无名引用
对于一次性的,带参数表达式,用LB可以节省不必要的class定义和维护,简化程序的设计-维护代价。
比如下面的vector处理代码,简洁明了:
vector<int> v1(10, 1);
int sum = 0;
for_each (v1.begin(), v1.end(), [&](int i){ sum += i; });//Line1
否则,我们必须定义一个function类,把如此简单的事情复杂化。用了LB,我们把定义function 类的工作,转交给编译。VC++中,上述LB编译的实现是产生一个隐身类:
class _lambda_a01 {
int &capture1_;
public:
_lambda_a01(int &x): capture1_(x) {} //Line2
operator void (int i) { capture1_ += I; }
};
在引用时(Line1),它变成:
_lambda_a01 lbd1(sum);
for(auto a:v1){
ldb1(a);
}
读者也许好奇,为什么C++不直接把LB转换成inline expression (inline 表达式),而是要生成一个隐身类呢?这是因为LB的确可以当成“type”变量来用,这样使得LB和其他类有了同等地位。比如:
vector<int> v1(10, 1);
int sum = 0;
for_each (v1.begin(), v1.end(), [&](int i){ sum += i; });//Line1
vector<int> v2(10, 1);
int sum2 = 0;
for_each (v1.begin(), v1.end(), [&](int i){ sum2 += i; });//Line2
我们如果用上述的方法,Line1和Line2重复代码,是软件工程的大忌。我们可以用下列LB使用模式:
有名无型引用
vector<int> v1(10, 1);
vector<int> v2(10, 1);
int sum = 0;
auto lb = [&](int i){ sum += i; }; //Line0
for_each (v1.begin(), v1.end(), lb);//Line1
sum = 0; // Line1.1
for_each (v1.begin(), v1.end(), lb});//Line2
在Line0,我们定义了一个有名(lb)无型的LB,可以在Line1和Line2重复使用。
注意的是,
1) 每个LB的“定义”都会产生新的“隐身”类,所以尽量用“有名引用”,会减少代码的size,缩小工作集。
2) 定义时,LB一次性“俘获”环境变量,所以上面修改后的代码加了Line1.1,以便正确表达应用逻辑。
3) 俘获可以是“传值(by value)”也可以是“传引用(by reference)。我们Line0用的是by reference.
有名有型引用
上面两种LB使用模式,是LB应用的主要模式,它直接反映出了LB的优点。另一方面说,既然LB无非是隐身类,我们没有理由不能把它当作普通变量使用。这个模式是一种简化的functor使用模式。我们可以把LB定义成一个std::function,比如上面的auto lb可以定义成:
std::function <void(int)> lb; //lb is a function which takes an integer and returns void
注意到用这个定义,使得我们可以推迟给LB变量赋值,甚至一变量赋多址(不同时间)。下面就是一个简单用例:
struct MyLambda
{
std::function <int (int)> _lbda;//line1
int _extra;
};
MyLambda TestLambdaObj(int t)
{
MyLambda ret;
if (t == 1)
{
ret._extra = t;
ret._lbda = [=](int x) -> int { return t + x; }; //line2
return ret;
}
else
{
ret._extra = t;
ret._lbda = [=](int x) -> int { return t * x; };//line3
return ret;
}
}
void TestLambdaFun2(int t)
{
MyLambda ret = TestLambdaObj(t);
int v = ret._lbda(t); //line4
printf("v is '%d' for type %d", v, t);
}
我们先定义MyLambda数据类,并与其定义了一了function成员_lbda,根据C++ SPEC,他可以由LB转换构造,并且和普通的类变量无甚区别。然后我们可以运行时给它赋值(line2,line3), 当作普通function来使用(line4)。
注意的是:
- function的定义中没有“闭包”的概念,闭包的形成是在LB创建时实现(line2,line3)。
- 把LB赋值给function变量,必然造成调用时(line4)的间接性(通过函数指针),其性能相当于虚拟函数,也不能inline化,当然比直接调用有所下降。
闭包(closure)是LB的独特附加值
如果你问为什用LB而不用std::function?我的回答是“闭包”。
C++用LB来实现闭包,是一个简化繁琐的class初始化的syntax sugar。这一点是std::function所不可替代的。比如说:
auto sum = 0;
auto step = 2;
auto lb = [&](int i){ sum += i + step; }//capture sum and step by ref
lb形成自己的闭包,自动从环境中俘获了sum和step,若用class实现,上面的程序起码增加10行代码。
LB性能初探
下面的简单程序,测试四种功能完全一样,但使用不同表达式的逻辑:
1)t =1 时用LB,
2)t=2 时用直接表达式
3)t=3 时用函数
4)t=4时用std::function间接调用LB
void TestLambdaFun(int t)
{
using namespace std;
vector<int> v1(10, 1);
int x = 0;
int u = 0;
if (t == 1)
{
clock_t begin = clock();
for (int i = 0; i < 100000; ++i)
{
for_each (v1.begin(),
v1.end(),
[&x, &u](int i){ u += i+(x++); });// Line 1
}
clock_t end = clock();
auto spent = double(end - begin) / CLOCKS_PER_SEC;
printf("spent for type '%d' is %f u is %d\n", t, spent, u);
}
else if (t == 2)
{
clock_t begin = clock();
for (int i = 0; i < 100000; ++i)
{
auto _First = v1.begin();
auto _Last = v1.end();
for (; _First != _Last; ++_First)
{
u = *_First+(x++); // Line 2
}
}
clock_t end = clock();
auto spent = double(end - begin) / CLOCKS_PER_SEC;
printf("spent for type '%d' is %f u is %d\n", t, spent, u);
}
else if (t == 3)
{
clock_t begin = clock();
for (int i = 0; i < 100000; ++i)
{
auto _First = v1.begin();
auto _Last = v1.end();
for (; _First != _Last; ++_First)
{
FuncAdd(u, x, *_First); // Line 3
}
}
clock_t end = clock();
auto spent = double(end - begin) / CLOCKS_PER_SEC;
printf("spent for type '%d' is %f u is %d\n", t, spent, u);
}
else if (t == 4)
{
clock_t begin = clock();
std::function <void (int)> lbda;
for (int i = 0; i < 100000; ++i)
{
lbda = [&](int i){ u += i + (x++); };
for_each (v1.begin(), v1.end(), lbda); // Line 4
}
clock_t end = clock();
auto spent = double(end - begin) / CLOCKS_PER_SEC;
printf("spent for type '%d' is %f u is %d\n", t, spent, u);
}
}
void FuncAdd(int &u, int &x, int i)
{
u = i+(x++);
}
下面是VC++ 2010中的测试结果:
- 在debug模式下,t=2时速度最快,这是因为t=1,t=3,t=4时都是用了函数调用,性能当然不及inline表达式。
- 在release模式下(选择/Ob1优化,对inline函数进行inline扩展)
- t=1和t=2速度完全一样,比t=3时平均快3倍。当然,我们也可以把FuncAdd inline化。这里的主要目的,是证明优化后,LB的性能和表达式完全一样。证明C++ lambda expression不是浪得虚名的隐身类的syntax sugar,而是名副其实的“表达式”。
t=4最慢,它和t=3类似。但是由于通过了std::function的虚拟函数表间接调用,/Ob1优化失去作用,使它不但要调用一个() operator,而且是通过“虚拟表”间接调用。所以从性能上说,把LB通过std::function间接使用,失去了LB的性能优势。
总结
C++ 11 的lambda expression(简称LB),在可以保证和inline expression同样性能的条件下,增加了参数功能和闭包功能,是我们写出简洁,明了,易维护代码的绝佳工具。应用时,为了避免代码重复和增加隐身类的数量,可用有名无型的LB变量。LB也可以赋值于std::function,当作函数指针使用,但是性能不及简单地inline使用。
转载于:https://www.cnblogs.com/ly8838/p/3969426.html
浅谈C++ Lambda 表达式(简称LB)相关推荐
- 浅谈lambda表达式最通俗易懂的讲解
来自:开源中国(作者:青衣霓裳) 原文链接: https://my.oschina.net/u/4006148/blog/3078359 Java8发布已经有一段时间了,这次发布的改动比较大,很多人将 ...
- Lambda 表达式浅谈- 01
已经有一段时间没有发布博文了... 今天就写一写lambda的一些简单的使用方法 Lambda 在Msdn 上的描述: Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数. 通过使用 ...
- 浅谈Java8之lambda表达式
1.首先我们看一个通常喜欢写的一个示例 new Thread(new Runnable() {@Overridepublic void run() {System.out.println(" ...
- 浅谈Java行为参数化和Lambda表达式
案例分析 在软件工程中,一个众所周知的问题就是,不管你做什么,用户的需求肯定会变.比方说,有个应用程序是帮助农民了解自己的库存的.这位农民可能想有一个查找库存中所有绿色苹果的功能.但到了第二天,他可能 ...
- 一起谈.NET技术,从.NET中委托写法的演变谈开去(中):Lambda表达式及其优势...
在上一篇文章中我们简单探讨了.NET 1.x和.NET 2.0中委托表现形式的变化,以及.NET 2.0中匿名方法的优势.目的及注意事项.那么现在我们来谈一下.NET 3.5(C# 3.0)中,委托的 ...
- 255.0.0.0子网掩码相应的cidr前缀表示法是?_【洛谷日报#246】浅谈表达式的求值(Vol.2 进阶)...
Warning 在观看本博客之前,请保证自己理解了表达式的三种表达方式. 本文旨在让大家更深层次的了解表达式,基础的知识就是上方的链接中所写的.所以,在了解后缀表达式的运算原理之后,我将不会讲述类似的 ...
- 从.NET中委托写法的演变谈开去(中):Lambda表达式及其优势
在上一篇文章中我们简单探讨了.NET 1.x和.NET 2.0中委托表现形式的变化,以及.NET 2.0中匿名方法的优势.目的及注意事项.那么现在我们来谈一下.NET 3.5(C# 3.0)中,委托的 ...
- laytpl语法_浅谈laytpl 模板空值显示null的解决方法及简单的js表达式
浅谈laytpl 模板空值显示null的解决方法及简单的js表达式 laytpl 模板语法 {{ d.field }} 输出一个普通字段,不转义html 官方的说明 但d.field 为空时会显示nu ...
- 浅谈λ演算与Python的lambda函数
快速浏览 浅谈λ演算与Python中的lambda函数 λ演算 初识λ演算 λ演算的句法结构 两个特征 写lambda项的三个(递归的)规则 等价变换lambda项的三个法则 α-等价 β-归约 η- ...
- python编写函数_浅谈Python 函数式编程
匿名函数lambda表达式 什么是匿名函数? 匿名函数,顾名思义就是没有名字的函数,在程序中不用使用 def 进行定义,可以直接使用 lambda 关键字编写简单的代码逻辑.lambda 本质上是一个 ...
最新文章
- MySQL删除用户权限(REVOKE)
- Java,Math类中的ceil、floor和round函数源码解析以及自己重写实现
- [Python图像处理] 十六.图像的灰度非线性变换之对数变换、伽马变换
- 1029:计算浮点数相除的余
- 4g模块注册上网 移远_通信模组企业 移远通信amp;广和通
- python3 规则引擎_几个常见规则引擎的简单介绍和演示
- master分支删除文件_Git分支基础简介;创建分支;合并分支;删除分支;
- 双盘转子动力学仿真c语言程序,递归牛顿欧拉(正)动力学仿真
- 【微服务架构】SpringCloud组件和概念介绍(一)
- TypeScript和vuejs 搭建webpack,初次体验
- 神经网络中的前向和后向算法
- golang 安装 guru vscode 安装失败
- 此计算机中未配置默认浏览器,飞火浏览器设置默认浏览器失败了怎么办-飞火浏览器设置默认浏览器的方法 - 河东软件园...
- centos7下学习Redis(一)
- 异步IO框架实现之完成端口(Completion Port)
- 小程序毕设作品之微信电子书阅读小程序毕业设计(7)中期检查报告
- 前端从一只小白到工作半年的心路历程
- Web 前端从入门菜鸟到实践老司机所需要的资料与指南合集
- Substance Painter材质导入unity渲染通道配置更改
- ROSCon会议详细资料
热门文章
- android--------自定义控件 之 组合控件篇
- 社会管理不妨向大数据要效率
- CentOS下使用TUN/TAP虚拟网卡的基本教程
- 用户空间缺页异常pte_handle_fault()分析--(上)【转】
- JavaScriptJquery 练习 扫雷
- 使用 C# 开发智能手机软件:推箱子(十)
- An Image is worth 16x16 words:transformers for image recognition at scale
- RFC792翻译(ICMP主要内容)
- Kafka负载均衡、Kafka自定义Partition、Kafk文件存储机制
- 【18.40%】【codeforces 631D】Messenger