一. lambda表达式

(一)语法定义[capture](paramters) mutable ->returnType{statement}

  1.[capture]:捕获列表

  (1)lambda函数只能捕获父作用域中的局部变量或形参。而捕获非父作用域或静态变量则会出错。(这里的父作用域指的是包含lambda函数的语句块,如main函数作用域)

    ①[]:表示不捕获;[=]和[&]:分别表示按值和按引用捕获所有父作域变量(包括this);

    ②[var]、[&var]分别表示按值和按引用捕获var。注意,默认是无法修改按值捕获的变量的值(因为lambda表达式的operator()默认为const)。

    ③[=,&foo]:表示按引用捕获foo变量,按值捕获父作用域中所有其它变量。

    ④[this]:捕获当前类中的this指针,让lambda表达式拥有和当前类成员函数同样访问权限。捕获this的目的是可以在lambda中会使用当前类的成员函数和成员变量。

  (2)注意事项:

    ①捕获列表不允许变量重复传递。如[=,a]、[&,&this]其中的a和this都被重复传递。

    ②lambda表达式的按值捕获,是在声明lambda表达式的一瞬间就被复制了如果希望lambda表达式在调用时能即时的访问外部变量,应该使用按引用捕获

    ③默认情况下,按值捕获的变量是不可以被修改的,因为lambda表达式的operator()是个const函数。

    ④lambda不能捕获非父作用域变量或static变量。即它们不能被进入捕获列表中,但可在lambda的函数体内直接访问。

   2.(parameters):参数列表。

  (1)与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号()一起省略。

  (2)参数列表不支持默认值,也不支持可变参数。所有的参数必须有参数名。

  (3)C++14中,参数类型可以声明为auto类型

  3.mutable默认下,lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空)

   4.returnType返回值类型。用追踪返回类型形式声明函数的返回类型。在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行自动推导。如果没有return语句则返回void。

  5.{statement}函数体。内容与普通函数一样,除了可以使用参数之外,还可以使用所有捕获的变量。

(二)lambda表达式与仿函数

  1. 仿函数是编译器实现lambda表达式的一种方式。在现阶段,通常编译器都会把lambda表达式转化成为一个仿函数对象。因此,在C++11中,lambda可以视为仿函数的一种等价形式或叫“语法糖”。

  2. 两者虽然在语法层面上不同,但却有着相同的内涵为——都可以捕捉一些变量作为初始状态并接受参数进行运算。

  3. lambda表达式在C++11中被称为“闭包类型(Closure Type)”,可以认为是个仿函数,带有const属性的operator()它的捕获列表捕获的任何外部变量最终均会变为仿函数的成员变量。由闭包类型定义的对象称为“闭包”(是个右值)。

  4. 没有捕获变量的lambda表达式可以直接转换为函数指针,而捕获变量的lambda表达式则不能转换为函数指针

【编程实验】lambda初体验

  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. int gVal = ;
  6.  
  7. //捕获this指针
  8. class Test
  9. {
  10. private:
  11. int i = ;
  12. public:
  13. void func(int x, int y)
  14. {
  15. int a = ;
  16. //auto lamb1 = [] {return i; }; //error,无捕获列表。
  17. //auto lamb2 = [&i] {return i; }; //error, 不能捕获父作用域(func域)以外的变量(i)
  18. auto lamb3 = [=] {return i; }; //ok,按值捕获(含this指针),因此可以访问类中的成员变量(i)。
  19. auto lamb4 = [&] {return i + x + a; }; //ok,按引用捕获(含this指针),可以使用类中的成员变量(i)
  20. //同时,也捕获到形参x和局部变量a。
  21. auto lamb5 = [this] {return i; }; //ok,直接捕获this指针。
  22.  
  23. auto lamb6 = [] {return gVal++; }; //ok,可以使用直接使用全局变量,无须也不能捕获它。
  24. }
  25. };
  26.  
  27. int main()
  28. {
  29. int a = ;
  30. int b = ;
  31.  
  32. //1. lambda表达式初体验
  33. auto lamb1 = [] {}; //最简单的lambda表达式
  34.  
  35. auto lamb2 = [=] {return a + b; };//省略参数列表和返回类型
  36. cout << lamb2() << endl; //
  37.  
  38. auto lamb3 = [&](int c) {b = a + c; };//省略返回类型,为void。
  39. //cout << lamb3(5) << endl; //error,返回void
  40.  
  41. auto lamb4 = [] {return ; }; //省略参数列表
  42. cout << lamb4() << endl;
  43.  
  44. auto lamb5 = [=, &b](int c)->int {return b += a + c; }; //各部分完整的lambda表达式
  45. cout << "lamb5(2) = "<<lamb5() << ", b = " << b<< endl; //9, 9
  46.  
  47. //2. lambda表达式的常量性及mutable关键字
  48. a = ;
  49. //auto f1 = [] {return a++; }; //error,没有捕获外部变量
  50. //auto f2 = [=]() { a = 1;}; //error,const函数不能修改按值捕获的变量
  51. auto f2 = [=]() mutable { a = ; }; //ok,被mutable修饰
  52. auto f3 = [&a]() { a = ; }; //ok,按引用传递。const函数时影响引用本身,表示其不可修改
  53. //但其引用的内容不受const影响,仍可修改。
  54. //3. 捕获的时间点
  55. int x = ;
  56. auto lambByVal = [x] {return x + ; }; //按值捕获:声明时,x被复制一下
  57. auto lambByRef = [&x] {return x + ; };//按引用捕获:x的值是随外部x的变化而变化。
  58. cout << "lambByVal() = "<< lambByVal() << endl; //
  59. cout << "lambByRef() = "<< lambByRef() << endl; //
  60.  
  61. ++x;
  62.  
  63. cout << "lambByVal() = " << lambByVal() << endl; //
  64. cout << "lambByRef() = " << lambByRef() << endl; //12
  65.  
  66. //4. lambda表达式转换为函数指针
  67. using FuncX = int(*)(int);
  68. using FuncXY = int(*)(int, int);
  69.  
  70. int k = ;
  71. auto lambN = [](int x, int y) {return x + y; }; //无捕获列表
  72. auto lambK = [&k](int x, int y) {return x + y + k; }; //有捕获列表
  73.  
  74. FuncXY funcXY;
  75. funcXY = lambN; //ok,无捕获列表的lambda可转化为函数指针
  76. //lambN = funcXY; //error,不能将函数指针转为lambda
  77. //funcXY = lambK; //error,有捕获列表的lambda不能转为函数指针
  78.  
  79. //5. 捕获this指针(见Test类)
  80.  
  81. return ;
  82. }

初识lambda

(三)泛型lambda表达式

  1. 概述:

  (1)泛型lambda的格式形如 [](auto x, auto y){}; 或 [](auto&& x, auto&& y){} 等。

  (2)由于auto&&类型的形参没有可用的T类型,泛型lambda采用forward+decltype来转发param

  (3)当param被左值实参初始化时,param被推导为左值引用,即decltype(param)为左值引用类型。同理,当param被右值初始化时,即decltype(param)为右值引用类型。

  (4)decltype(param)作为模板形参传入std::forward时,会发生引用折叠,从而能正确根据实参的左/右值特性进行转发。

  2. 泛型lambda的优势

  (1)使用auto作为lambda函数的参数类型修饰符,增加泛型编程能力

  (2)泛型lambda允许带auto参数的lambda函数能够转化为函数指针

【编程实验】泛型lambda

  1. #include <iostream>
  2. #include <vector>
  3. #include <algorithm>
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. //1. 泛型局部函数
  9. auto f1 = [](auto x, auto y) {return x + y; };
  10. cout <<"f1(1, 3) = " << f1(, ) << endl;
  11. cout <<"f1(4.5, 6.3) = " << f1(4.5, 6.3) << endl;
  12. cout << "f1(\"abc\", \"def\") = " << f1(string{ "abc" }, "def") << endl;
  13.  
  14. //2. 泛型回调函数
  15. auto f2 = [](auto x) {cout << x << ","; };
  16. vector<int> v1{, , };
  17. vector<string> v2{ "a","b","c" };
  18. for_each(v1.begin(), v1.end(), f2); cout << endl;
  19. for_each(v2.begin(), v2.end(), f2); cout << endl;
  20.  
  21. //3. 泛型lambda与函数指针的转换
  22. auto f3 = [](auto x){ return x; };
  23. using Func = int(*)(int);
  24. Func pf = f3;
  25. cout << pf() << endl; //
  26.  
  27. cout << "----------------------------------"<< endl;
  28. //4. 利用泛型lambda模拟实现tuple。
  29. //(1)make_tuple接受可变参数包,返回一个tuple对象(lambda)。
  30. // 如,auto tp = make_tuple(1, '2', "3");即tp = [](auto access){return access(1, '2', "3")};
  31. // 可见参数包己被展开,并保存在tp中,可随时供访问器使用。
  32. //(2)该tuple的使用方式是:tuple(access),向其传入某种功能的访问器(如打印,求长度函数)
  33. // 这种通过tuple+access的方式可以达到访问tuple中元素的目的。注意,access为可接受可变参数包的
  34. // 可调用对象。
  35. auto make_tuple = [](auto ...xs) {
  36. return [=](auto access) { return access(xs...); };
  37. };
  38.  
  39. //4.1 求tuple的长度
  40. auto length = [](auto xs) { //length(tp),传入tuple
  41. return xs([](auto ...z) { return sizeof...(z); }); //通过tp(access)向其传入计算元素个数的访问器。
  42. };
  43.  
  44. //4.2 fmap的功能: 将一个tuple(tpSrc)通过func函数映射成另一个新的tuple。如通过对元素*2,
  45. //将tuple(1,2,3,4)映射成tuple(2,4,6,8)
  46. auto fmap = [=](auto func) { //捕获make_tuple
  47. return [=](auto tpSrc) { //捕获func和make_tuple
  48. return tpSrc([=](auto... xs) { return make_tuple(func(xs)...); }); //tuple+访问器方式
  49. };
  50. };
  51.  
  52. auto tp = make_tuple(, '', "");
  53. std::cout << length(tp) << std::endl; //
  54. int len = tp([](auto...z) { return sizeof...(z); }); //通过向tuple传入访问器的方式来使用tuple。
  55. //注意访问器必须可接受可变参数包。
  56. std::cout << len << std::endl;
  57.  
  58. auto twice = [](auto i) { return * i; }; //映射函数
  59. auto print = [](auto i) { std::cout << i << " "; return i; };
  60. auto tp1 = make_tuple(, , , );
  61. auto tp2 = fmap(twice)(tp1); //将tp1通过twice函数映射成tp2
  62. auto tp3 = fmap(print)(tp2); //将tp2通过print函数映射成tp3,并通过print将tp2元素打印出来。
  63. //make_tuple(func(xs)...) 等价于make_tuple(func(xs1),func(xs2),...)
  64. //因参数按从右向左依次传入,所以最终打印结果为8,6,4,1
  65.  
  66. return ;
  67. }
  68. /*输出结果
  69. f1(1, 3) = 4
  70. f1(4.5, 6.3) = 10.8
  71. f1("abc", "def") = abcdef
  72. 1,2,3,
  73. a,b,c,
  74. 5
  75. ----------------------------------
  76. 3
  77. 3
  78. 8 6 4 2
  79. */

二. lambda表达式的优势

(一)使代码更加简化、逻辑更清晰。

(二)使程序更灵活,在需要的时间和地点实现闭包

(三)简化仿函数的使用,使得STL算法的使用更加容易

【编程实验】lambda的优势

  1. #include <iostream>
  2. #include<algorithm>
  3. #include <functional>
  4.  
  5. using namespace std;
  6. using namespace std::placeholders;
  7.  
  8. int g_ubound = ;
  9. vector<int> nums = { , , , , , , , , ,,,, };
  10. vector<int> largeNums;
  11.  
  12. //显示vector中的元素
  13. void print(vector<int>& vec)
  14. {
  15. for (auto& elem : vec) {
  16. cout << elem << " ";
  17. }
  18.  
  19. cout << endl;
  20. }
  21.  
  22. //函数
  23. inline void LargNumsFunc(int i)
  24. {
  25. if (i > g_ubound)
  26. {
  27. largeNums.push_back(i);
  28. }
  29. }
  30.  
  31. //仿函数
  32. class LargeNums
  33. {
  34. private:
  35. int ubound;
  36. public:
  37. LargeNums(int u):ubound(u){}
  38.  
  39. void operator()(int i) const
  40. {
  41. if (i > ubound)
  42. {
  43. largeNums.push_back(i);
  44. }
  45. }
  46. };
  47. void test(int ubound)
  48. {
  49. //1. 使用传统的for(缺点:需要直接使用全局变量g_ubound)
  50. for (auto iter = nums.begin(); iter != nums.end(); ++iter) {
  51. if (*iter > ubound) {
  52. largeNums.push_back(*iter);
  53. }
  54. }
  55. print(largeNums);
  56.  
  57. largeNums.clear();
  58. //2.使用函数指针
  59. //缺点:函数定义在别的地方,代码阅读不方便。inline非强制性的,内联不一定成功。可
  60. // 能导致性能问题。且LargeNumFunc由于使用了全局变量,是个有状态函数,函数重用性不高。
  61. for_each(nums.begin(), nums.end(), LargNumsFunc);
  62. print(largeNums);
  63.  
  64. largeNums.clear();
  65. //3. 使用仿函数
  66. //优点:仿函数可以拥有状态,由于for_each第3个参数只能传递一个可调用对象而不能传递额外的参数。
  67. // 因此,利用仿函数就可以克服这一不足。
  68. //缺点:需要单独定义一个仿函数类。
  69. for_each(nums.begin(), nums.end(), LargeNums(g_ubound));
  70. print(largeNums);
  71.  
  72. largeNums.clear();
  73. //4. 使用lambda表达式
  74. //优点:比仿函数书写上更简洁,代码的功能更清晰。
  75. for_each(nums.begin(), nums.end(), [ubound](int i)
  76. {
  77. if (i > ubound) {
  78. largeNums.push_back(i);
  79. }
  80. });
  81. print(largeNums);
  82. }
  83. int main()
  84. {
  85. //1. lambda可简化标准库的调用(统计(50,73]之间的元素个数)
  86. vector<int> v{ , , , , , , , };
  87. //1.1 组合使用bind
  88. auto f1 = std::bind(std::logical_and<bool>(),
  89. std::bind(std::greater<int>(), _1, ),
  90. std::bind(std::less_equal<int>(), _1, )
  91. );
  92. cout << count_if(v.begin(), v.end(), f1) << endl; //2
  93.  
  94. //1.2 使用lambda表达式
  95. auto f2 = [](int x) {return (<x) && (x<=); };
  96. cout << count_if(v.begin(), v.end(), f2) << endl; //
  97. int cnt = count_if(v.begin(), v.end(), [](int x)
  98. {
  99. return ( < x) && (x <= );
  100. });
  101. cout << cnt << endl; //2
  102.  
  103. //2. lambda与其他可调用对象使用上的比较
  104. test(g_ubound);
  105.  
  106. return ;
  107. }
  108. /*输出结果
  109. 2
  110. 2
  111. 2
  112. 11 12 13 14 15 16 17 18 19 20
  113. 11 12 13 14 15 16 17 18 19 20
  114. 11 12 13 14 15 16 17 18 19 20
  115. 11 12 13 14 15 16 17 18 19 20
  116. */

第17课 lambda表达式的更多相关文章

  1. 第13课 lambda表达式

    1. lambda的语法形式:[capture](params) opt -> ret {body;}; (1)capture为捕获列表 ①[].[&]和[=]分别表示不捕获.按引用捕获 ...

  2. 17、lambda表达式

    一.简介 lambda表达式允许你通过表达式来代替功能接口,lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块),它还增强了集合 ...

  3. JDK1.8新特性之(一)--Lambda表达式

    近期由于新冠疫情的原因,不能出去游玩,只能在家呆着.于是闲来无事,开始阅读JDK1.8的源代码.在开始之前也查询了以下JDK1.8的新特性,有针对性的开始了这段旅程. 只看不操作,也是不能心领神会的. ...

  4. C# Lambda 表达式学习之(四):动态构建类似于 c => c.Age == 2 || c.Age == 5 || c => c.Age == 17 等等一个或多个 OrElse 的表达式

    可能你还感兴趣: 1. C# Lambda 表达式学习之(一):得到一个类的字段(Field)或属性(Property)名,强类型得到 2. C# Lambda 表达式学习之(二):LambdaExp ...

  5. 01 语言基础+高级:1-7 异常与多线程_day07 【线程池、Lambda表达式】

    day07[线程池.Lambda表达式] 主要内容 等待与唤醒案例 线程池 Lambda表达式 教学目标 -[ ] 能够理解线程通信概念-[ ] 能够理解等待唤醒机制-[ ] 能够描述Java中线程池 ...

  6. 17.继承 and18.接口和多态 内部类 匿名内部类,Lambda表达式

    1. 继承 1.1 继承的实现(掌握) 继承的概念 继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法 实现继承的格式 继承通过extends实现 ...

  7. 程序猿修仙之路--数据结构之你是否真的懂数组? c#socket TCP同步网络通信 用lambda表达式树替代反射 ASP.NET MVC如何做一个简单的非法登录拦截

    程序猿修仙之路--数据结构之你是否真的懂数组?   数据结构 但凡IT江湖侠士,算法与数据结构为必修之课.早有前辈已经明确指出:程序=算法+数据结构  .要想在之后的江湖历练中通关,数据结构必不可少. ...

  8. 第19课 lambda vs std::bind

    一. std::bind (一)std::bind实现的关键技术 [编程实验]探索bind原理,实现自己的bind函数 #include <iostream> #include <t ...

  9. lambda表达式

    什么是Lambda表达式 lambda表达式,它将允许我们将行为传到函数里.在Java 8之前,如果想将行为传入函数,仅有的选择就是匿名类,需要6行代码.而定义行为最重要的那行代码,却混在中间不够突出 ...

随机推荐

  1. Window权限维持(五):屏幕保护程序

    屏幕保护是Windows功能的一部分,使用户可以在一段时间不活动后放置屏幕消息或图形动画.众所周知,Windows的此功能被威胁参与者滥用为持久性方法.这是因为屏幕保护程序是具有.scr文件扩展名的可 ...

  2. java架构之路-(tomcat网络模型)简单聊聊tomcat(二)

    上节课我们说到的Tomcat,并且给予了一般的tomcat配置,和配置的作用,提到了HTTP/1.1 也就是我们的网络通讯模型,那么HTTP/1.1又代表什么呢.我们来简答看一下. tomcat有四种 ...

  3. 使用SolrJ(即java客户端)开发Solr。

    1.什么是SolrJ呢? 答:Solrj是访问Solr服务的java客户端,提供索引和搜索的请求方法,SolrJ通常在嵌入在业务系统中,通过SolrJ的API接口操作Solr服务.开始配置schema ...

  4. Asp.Net中Global报错,关键字也不变色问题

    原因是我把Global名字改了,使用默认名字就好了

  5. python 跟踪IP模块

    #coding=utf-8 import re import subprocess def tracertIP(ip): p = subprocess.Popen(['tracert',ip],std ...

  6. 去除数组空格 php

    public function trimArray($params){ if (!is_array($params)) return trim($params); return array_map([ ...

  7. OL7.7安装Oracle 11.2.0.4

    安装环境准备工具 yum –y install oracle-rdbms-server-11gR2-preinstall 创建目录 mkdir -p /u01/app/oracle/product/1 ...

  8. ZAP 代理 Chrome 系统 win10

    ZAP 代理原理 如下浏览器,拿Chrome为例,Chrome发出的请求都会先经过 ZAP, 然后再由 ZAP 发往服务器.如下图: Chrome 设置 1. Chrome设置只需要在地址栏输入 ch ...

  9. centos7 安装gitlab及简单配置

    1.安装 wget -O gitlab.rpm https://packages.gitlab.com/gitlab/gitlab-ce/packages/el/7/gitlab-ce-11.11.3 ...

  10. 【MySQL高可用架构设计】(一)-- mysql复制功能介绍

    一. 介绍 Mysql的复制功能是构建基于SQL数据库的大规模高性能应用的基础,主要用于分担主数据库的读负载,同时也为高可用.灾难恢复.备份等工作提供了更多的选择. 二.为什么要使用mysql复制功能 ...