1. lambda的语法形式:[capture](params) opt -> ret {body;};

(1)capture为捕获列表

  ①[]、[&]和[=]分别表示不捕获、按引用捕获、按值捕获所有父作用域中内的局部变量。(父作用域指包含lambda表达式的语句块,如main函数)。

    ◆lambda函数只能捕获父作用域中的局部变量,而捕获非父作用域static变量都会报错(不是C++11的标准,其行为可能因编译器而不同)。(注意全局变量或static变量不能被捕获。即不能被写入捕获列表中,但可在lambda的函数体内直接访问

    ◆默认下无法修改按值捕获的外部变量因为lambda表达式的operator()默认是const函数,但捕获this指针后可修改成员变量的值,因为是通过this指针来修改的)。

    ◆在类中如果使用&或=捕获,会同时默认捕获this指针。

  ②[=,&foo]按值捕获外部作用域中所有变量,并按引用捕获foo变量。注意,捕获列表不允许变量重复传递,如[=,var],var被按值捕获了两次,这是不允许的。

  ③[bar]按值捕获bar变量(注意只捕获bar,其他变量不被捕获)

  ④[this]捕获当前类中的this指针,让lambda表达式拥有和当前类成员函数同样访问权限,可以使用当前类的成员函数和成员变量(注意也可修改non-const成员变量的值)。

(2)params: lambda 表达式的参数列表

  ①如果省略,则类似于无参函数func()

  ②参数列表中不能有默认参数所有的参数必须有参数名

  ③不支持可变参数。

(3)opt选项:

  ①mutable修饰符:默认下,lambda表达式的operator()是const函数mutable可以取消其常量性,让body内的代码可以修改被捕获的变量,并可以访问被捕获对象的non-const函数。在使用该修饰符时,参数列表不可省略(即使参数为空)

  ②exception:说明lambda表达式是否抛出异常(noexcept),以及抛出何种异常。如抛出整数类型的异常,可以使用throw(int)。

  ③attribute用来声明属性

(4)ret为返回值类型

  ①如果被省略了则由return语句的返回类型确定

  ②如果没有return语句,则类似于void func(…)函数

(5)body:函数体

【编程实验】lambda表达式初体验

#include <iostream>

using namespace std;

int g = ;

class Test
{
private:
int i = ;
public: void func(int x, int y)
{
//auto x1 = []{return i;}; //error,没有捕获任何变量。当然也无法捕获父作用域(func)以外的变量,因此i是不能访问到的!
//auto x1 = [&i]{return i;}; //error,无法捕获父作用域(func)以外的变量
auto x1 = [=]{return i++;}; //ok,因为“=”或“&”默认会捕获this指针,也就可以访问到成员变量。根据按值捕获的特点,此时
//在lambda的函数体内不能修改this指针本身。但这不影响我们通过this指针修改成员变量i的值! auto x2 = []{return g++;}; //ok,g不在lambda的父作用域,不能被捕获。
//如auto x2 = [&g]{return g++;}。但由于g是全局变量
//所以在lambda的body内仍是可见的! auto x3 = [=]{return i++, i + x + y;};//ok,按值捕获所有外部变量,由于&或=捕获时会默认地同时传入this,所以可改变i的值
auto x4 = [&]{return i + x + y;}; //ok,按引用捕获所有变量:x, y以及this指针
//但注意,i没有被捕获,也不可捕获到。它是通过this指针来访问到的。 auto x5 = [this]{return i++;}; //ok,捕获了this指针,也就可以修改成员的值
//auto x6 = [this, x]{return i + x + y;}; //error,没捕获y
}
}; int main()
{
[]{}; //最简单的lambda表达式 //使用返回值后置语法
auto f1 = [](int a) ->int {return a + ;};
cout << f1() << endl; //输出2 //省略了返回值类型,由return语法推断
auto f2 = [](int i){return i + ;}; //注意:初始化列表不能用于返回值的自动
//推导:如auto x = [](){return {1, 2};};
cout << f2() << endl; //输出2 //参数列表为空时可以省略
auto f3 = []{return ;}; //等价于 auto f3 = [](){return 1;};
cout << f3 << endl; //等价于 cout << f3() << endl; int a = , b = ;
//auto f4 = []{return a;}; //error,没有捕获外部变量
auto f5 = [&]{return a++;}; //ok,a=1;
//auto f6 = [=]{return a++;}; //error,按值捕获的
auto f7 =[a, &b]{return a + (b++);};//ok,按值捕获a,按引用捕获b++ return ;
}

2. lambda与仿函数

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

(2)注意事项

  ①lambda表达式按值捕获了所有外部变量。在捕获的一瞬间,变量x的值就已经被复制了。如果希望lambda表达式在调用时能即时访问外部变量,应当使用引用方式捕获。(如变量y)

  ②默认情况下,按值捕获的变量是不可以被修改的(如mx++会报错)因为operator()是const函数除非在lambda表达式加关键字mutable,此时重载的operator()就不会被加上const。但应注意,由于按值捕获的变量是外部变量的副本,修改他们并不会真正影响到外部变量。

  ③lambda表达式在C++11中被称为“闭包类型(Closure Type)”,可以认为它是一个带有operator()的类(即仿函数),它的捕获列表捕获的任何外部变量最终均会变为闭合类型的成员函数。没有捕获变量的lambda表达式可以直接转换为函数指针而捕获变量的lambda表达式则不能转换为函数指针。

【编程实验】深入分析lambda表达式

#include <iostream>

using namespace std;

int main()
{
//1. 按值和按引用捕获的比较
int a = ;
auto f_val = [=] {return a + ;}; //按值捕获:表达式中a的值是外部变量a的副本,
//在捕获一瞬间己确定下来。
auto f_ref = [&] {return a + ;}; //按引用捕获 cout << "f_val: " << f_val() << endl; //
cout << "f_ref: " << f_ref() << endl; // a++; //修改a值,a==13 cout << "f_val: " << f_val() << endl;//13,注意这里输出没变!
cout << "f_ref: " << f_ref() << endl;//14 //2. 修改按值捕获的变量(只能影响lambda表达式,不影响外部变量)
//auto f1 = [=]{erturn a++;}; //error,operator()是const函数!
auto f1 = [=]()mutable{cout << "++a: " << ++a << endl; return a;}; //必须写参数列表,即使为空!
cout << f1() << endl; //
cout << a << endl; //13 //3. lambda与函数指针的转换
using func_t = int (*)(int, int);
func_t f2 = [](int a, int b){return a + b;}; //ok,捕获列表必须为空!
cout << f2(, ) << endl; //30 //4. mutable关键字
int val = ;
//auto f3 = [=](){val = 3;}; //编译失败:const函数,不能修改按值捕获的变量
auto f3 = [=]()mutable{ val = ;};//ok,加了mutable关键字 auto f4 =[&](){val = ;}; //ok,依然是const函数,但可以修改按引用捕获的变
//量(不过没改动引用本身)
auto f5 = [=](int v) { v = ;}; //传参方式将val传
f5(val); //传参方式将val传入,与普通函数的传参方式等效! return ;
}

3. 使用lambda表达式简化代码

(1)lambda被设计出来的主要目的之一就是简化仿函数的使用,使得在调用标准库算法的调用时,可以不必定义函数对象,从而大大简化标准库的使用。

(2)lambda是就地封装的短小功能闭包,将其作为局部函数可以轻松地在函数内重用代码。使得代码更简洁,逻辑更清晰

【编程实验】使用lambda表达式,使代码更简洁、更灵活

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> using namespace std;
using namespace std::placeholders; const int g_ubound = ; vector<int> nums={, , , , , , , , , , , ,};
vector<int> largeNums; //显示vector元素
void show(vector<int>& vec)
{
for(auto& elem : vec){
cout << elem << " ";
} cout << endl;
} //函数指针
inline void LargeNumsFunc(int i)
{
if(i > g_ubound)
{
largeNums.push_back(i);
}
} //仿函数
class LargeNums
{
private:
int ubound;
public:
LargeNums(int u) : ubound(u){}; void operator() (int i) const
{
if(i > ubound){
largeNums.push_back(i);
}
}
}; //找出vector中大于ubound的元素
void test(int ubound)
{
//1. 使用传统的for
//缺点: 需要直接使用全局变量ubound
for(auto iter = nums.begin(); iter != nums.end(); ++iter){
if(*iter > ubound)
largeNums.push_back(*iter);
}
show(largeNums);
largeNums.clear(); //2. 使用函数指针:
//缺点:函数定义在别的地方,代码阅读不方便
// inline并非强制,可能导致效率问题,特别是循环次数较多的时候
// LargeNumsFunc是个有状态的函数,直接使用了全局变量(g_ubound),
// 函数的可重用性不高!
for_each(nums.begin(), nums.end(), LargeNumsFunc);
show(largeNums);
largeNums.clear(); //3. 使用仿函数
//优点: 仿函数可以拥有状态,由于for_each第3个参数的只能传递一个可调用对象,而不能
// 传递额外的参数很有利,因为可以将ubound作为仿函数的参数传入。
//缺点: 需要单独定义一个仿函数类
for_each(nums.begin(), nums.end(), LargeNums(ubound));
show(largeNums);
largeNums.clear(); //4. 使用lambda表达式
//优点: 比仿函数书写上更简便,函数的功能(找出大于ubound的元素)更清晰
for_each(nums.begin(), nums.end(), [=](int i){ //“=”会捕获test(int ubound)中的ubound变量
if(i>ubound)
largeNums.push_back(i);
});
show(largeNums);
largeNums.clear();
} int main()
{
//1. 简化标准库的调用(统计(50,73]之间元素的个数)
vector<int> v{, , , , , , , }; //组合使用bind
auto f1 = bind(logical_and<bool>(), bind(greater<int>(), _1, ),
bind(less_equal<int>(), _1, ));
cout << count_if(v.begin(), v.end(), f1) << endl; //2 //使用lambda表达式
auto f2 = [](int x)->bool {return (<x)&&(x<=);};
cout << count_if(v.begin(), v.end(), f2) << endl; //2
//cout << count_if(v.begin(), v.end(), [](int x)->bool {return (50<x)&&(x<=73);}) << endl; //2. lambda表达式与其他callable object的对比
test(g_ubound); return ;
}
/*输出结果
e:\Study\C++11\13>g++ -std=c++11 test3.cpp
e:\Study\C++11\13>a.exe
2
2
11 12 13 14 15 16 17 18 19 20
11 12 13 14 15 16 17 18 19 20
11 12 13 14 15 16 17 18 19 20
11 12 13 14 15 16 17 18 19 20
*/

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

  1. 第17课 lambda表达式

    一. lambda表达式 (一)语法定义:[capture](paramters) mutable ->returnType{statement} 1.[capture]:捕获列表 (1)lam ...

  2. Lambda表达式-使用说明

    jdk8已经发布4年,其中有一个特性:Lambda,它是一个令开发者便捷开发的一种方式,Lambda Expression (Lambda表达式)是为了让java提供一种面向函数编程,原本在jdk8之 ...

  3. java8:(Lambda 表达式简介)

    JDK8的新特性——Lambda表达式 JDK8已经发布快4年的时间了,现在来谈它的新特性显得略微的有点“不合时宜”.尽管JDK8已不再“新”,但它的重要特性之一——Lambda表达式依然是不被大部分 ...

  4. 快速了解Lambda表达式-Java

    目录 lambda表达式 前言 简介 简单入门 用法 好处 总结 lambda表达式 前言 最近因为疫情,也不能正常返校什么的,希望大家都能好好的,希望武汉加油,中国加油,在家也看了很多视频,学了一点 ...

  5. JavaSE学习笔记(13)---线程池、Lambda表达式

    JavaSE学习笔记(13)---线程池.Lambda表达式 1.等待唤醒机制 线程间通信 概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同. 比如:线程A用来生成包子的,线程B用 ...

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

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

  7. 第19课 lambda vs std::bind

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

  8. 3分钟入门lambda表达式

    本节是lambda表达式的一个入门课,讲解的同时配有练习demo 前言什么是lambda表达式?基础语法函数式接口自己实现一个函数式接口jdk提供的函数式接口Consumersupplierfunct ...

  9. 浅析匿名函数、lambda表达式、闭包(closure)区别与作用

    浅析匿名函数.lambda表达式.闭包(closure)区别与作用 所有的主流编程语言都对函数式编程有支持,比如c++11.python和java中有lambda表达式.lua和JavaScript中 ...

随机推荐

  1. 20165308 预备作业3 Linux安装及学习

    Linux安装及学习 Linux的安装 因为做的比较晚, 安装过程按照老师给出的步骤和同学指导并未出现很多问题,只是安装VirtualBox虚拟机增强功能时,代码没输正确,结果一直无法正确安装,后来也 ...

  2. Spring技术内幕总结 - AOP概述

    AOP是Aspect-Oriented Programming(面向方面/切面编程)的简称.Aspect是一种新的模块化机制,用来描述分散在对象.类或函数中的横切关注点.分离关注点使解决特定领域问题的 ...

  3. react 实现路由按需加载

    import() 方法: async.js 文件内容: import React from 'react'; // import "babel-polyfill"; //compo ...

  4. js获取上传图片大小,判断上传图片类型,获取图片真实宽度和高度

    html部分 <div class="form-group col-md-12"> <label class="col-md-2 text-right& ...

  5. Feign 自定义编码器、解码器和客户端

    Feign 的编码器.解码器和客户端都是支持自定义扩展,可以对请求以及结果和发起请求的过程进行自定义实现,Feign 默认支持 JSON 格式的编码器和解码器,如果希望支持其他的或者自定义格式就需要编 ...

  6. java的几种对象(po,dto,dao等)

    j2ee中,经常提到几种对象(object),理解他们的含义有助于我们更好的理解面向对象的设计思维.     POJO(plain old java object):普通的java对象,有别于特殊的j ...

  7. 分布式超级账本Hyperledger为什么选择使用kafka引擎实现共识方案

    使用kafka集群配置的原因也很简单,为orderer共识及排序服务提供足够的容错空间,当我们向peer节点提交Transaction的时候,peer节点会得到或返回(基于SDK)一个读写集结果,该结 ...

  8. 协程与多路io复用epool关系

    linux上其实底层都基于libevent.so模块实现的,所以本质一样 gevent更关注于io和其它 epool只是遇到io就切换,而gevent其它等待也切换

  9. 持续集成之Jenkins+Gitlab实现持续集成 [二]

    持续集成之Jenkins+Gitlab实现持续集成 [二] 项目:使用git+jenkins实现持续集成 开始构建  General  源码管理 我们安装的是Git插件,还可以安装svn插件  我们将 ...

  10. sql Find_IN_SET 用法

    字段以 1,2,3,4 格式存储的SELECT * from test where FIND_IN_SET('15',btype) GROUP_CONCAT + group_by