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 lbd1sum);

forauto av1{

   ldb1a);

}

 

读者也许好奇,为什么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

我们如果用上述的方法,Line1Line2重复代码,是软件工程的大忌。我们可以用下列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,可以在Line1Line2重复使用。

注意的是,

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转换构造,并且和普通的类变量无甚区别。然后我们可以运行时给它赋值(line2line3), 当作普通function来使用(line4)。

注意的是:

  • function的定义中没有“闭包”的概念,闭包的形成是在LB创建时实现(line2line3)。
  • 把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形成自己的闭包,自动从环境中俘获了sumstep,若用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使用。

浅谈C++ Lambda 表达式(简称LB)的更多相关文章

  1. 浅谈python lambda

    lambda x: x * x,实际上就是 def f(x): retrun x * x 关键字lambda表示匿名函数,冒号前的x表示函数参数. 匿名函数只能有一个表达式,不用写return,返回值 ...

  2. lambda 表达式的由来

    相关技术点:函数指针.C#委托.匿名方法.lambda表达式 谈到lambda表达式,首先从委托讲起, 委托是持有一个或者多个方法的对象,这个特性有点像C中的函数指针,可以指向不同的方法,下面的例子是 ...

  3. Lambda表达式 - 浅谈

    概述: 只要有委托参数类型的地方,就可以使用 Lambda表达式.在讲述Lambda表达式之前,有必要先简要说明一下 委托中的"匿名方法": using System; using ...

  4. 浅谈Lambda表达式详解

    lambda简介 lambda运算符:所有的lambda表达式都是用新的lambda运算符 " => ",可以叫他,“转到”或者 “成为”.运算符将表达式分为两部分,左边指定 ...

  5. Lambda 表达式浅谈- 01

    已经有一段时间没有发布博文了... 今天就写一写lambda的一些简单的使用方法 Lambda 在Msdn 上的描述: Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数. 通过使用 ...

  6. 浅谈lambda表达式&lt;最通俗易懂的讲解

    Java8发布已经有一段时间了,这次发布的改动比较大,很多人将这次改动与Java5的升级相提并论.Java8其中一个很重要的新特性就是lambda表达式,允许我们将行为传到函数中.想想看,在Java8 ...

  7. 浅谈sql 、linq、lambda 查询语句的区别

    浅谈sql .linq.lambda 查询语句的区别 LINQ的书写格式如下: from 临时变量 in 集合对象或数据库对象 where 条件表达式 [order by条件] select 临时变量 ...

  8. 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生

    [转].NET(C#):浅谈程序集清单资源和RESX资源   目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...

  9. 浅谈-Lambda

    Lambda简化了匿名委托的使用,让你让代码更加简洁,优雅.据说它是微软自c#1.0后新增的最重要的功能之一. 简介: lambda运算符:所有的lambda表达式都是用新的lambda运算符 &qu ...

随机推荐

  1. Linux负载均衡软件LVS之一(概念篇)

    一. LVS简介 LVS是Linux Virtual Server的简称,也就是Linux虚拟服务器, 是一个由章文嵩博士发起的自由软件项目,它的官方站点是www.linuxvirtualserver ...

  2. 数据结构(线段树):NOI 2016 区间

    [问题描述] [输入格式] [输出格式] [样例输入] 6 3 3 5 1 2 3 4 2 2 1 5 1 4 [样例输出] 2 [样例说明] [更多样例] 下载 [样例 2 输入输出] 见目录下的 ...

  3. Java---计算机贷款支付额计算(用对话框实现)

    本例演示如何编写程序来计算贷款支付问题. 下面是编写程序的步骤: 1.提示用户输入年利率.年数和贷款总额 2.利用年利率算出月利率 3.通过前面的公式计算月支付额. 4.计算总支付额,它是月支付额乘以 ...

  4. 我的学习笔记之node----node.js+socket.io实时聊天(1) (谨此纪念博客开篇)

    本想着从hello word开篇,也确实写了相关学习笔记.各种原因吧,现在又着急写出作品,便作罢. 这里将记录一个node.js+socket.io的实时聊天程序.(当然我也是跟着网上各种教程资料学习 ...

  5. Adjacent Bit Counts(动态规划 三维的)

    /** 题意: 给出一个01串 按照题目要求可以求出Fun(X)的值 比如: 111 Fun(111)的值是2: 输入: t (t组测试数据) n k (有n位01串 Fun()的值为K) 输出:有多 ...

  6. php 利用第三方软件进行网页快照

    网页快照有很多方法,具体的大家可以百度下.这里我复制一位别人的. 这里我只说下利用第三方软件(Web2Pic_Pro)实现. (1). 下载web2pic_pro软件.下载地址 http://isha ...

  7. 安装完sql server 后修改计算机名后不能进行发布的订阅的解决办法

    由于需要需要配置一个发布订阅,可是一直报告:" sql server 复制需要有实际的服务器名称才能连接到服务器,不支持通过别名.ip地址或其他任何备用名称进行连接.请指定实际的服务器名称“ ...

  8. 安装 Panda3D 并使用原有的Python

    Part 1:什么是Panda3D [原始网站] [中文版本] Part 2:注意事项 Panda3D的版本必须与Python相匹配 Part 3:使用已安装的Python 将‘C:\Panda3D- ...

  9. 2013ACM暑假集训总结-致将走上大三征途的我

    回想起这个暑假,从开始与雄鹰一起的纠结要不要进集训队,与吉吉博博组队参加地大邀请赛,害怕进不了集训队.当时激励我月份开始接触的,记得当时在弄运动会来着,然后就问了雄鹰一些输入输出的东西,怀着满心的期待 ...

  10. 从零开始学习jquery (二)

    前面我们了解到了如何获取使用jquery,下面我们主要看看jquery的一些语法.基本的语法 $(selector).action(). 美元符号定义 jQuery 选择符(selector)&quo ...