一. 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初体验

#include <iostream>

using namespace std;

int gVal = ;

//捕获this指针
class Test
{
private:
int i = ;
public:
void func(int x, int y)
{
int a = ;
//auto lamb1 = [] {return i; }; //error,无捕获列表。
//auto lamb2 = [&i] {return i; }; //error, 不能捕获父作用域(func域)以外的变量(i)
auto lamb3 = [=] {return i; }; //ok,按值捕获(含this指针),因此可以访问类中的成员变量(i)。
auto lamb4 = [&] {return i + x + a; }; //ok,按引用捕获(含this指针),可以使用类中的成员变量(i)
//同时,也捕获到形参x和局部变量a。
auto lamb5 = [this] {return i; }; //ok,直接捕获this指针。 auto lamb6 = [] {return gVal++; }; //ok,可以使用直接使用全局变量,无须也不能捕获它。
}
}; int main()
{
int a = ;
int b = ; //1. lambda表达式初体验
auto lamb1 = [] {}; //最简单的lambda表达式 auto lamb2 = [=] {return a + b; };//省略参数列表和返回类型
cout << lamb2() << endl; // auto lamb3 = [&](int c) {b = a + c; };//省略返回类型,为void。
//cout << lamb3(5) << endl; //error,返回void auto lamb4 = [] {return ; }; //省略参数列表
cout << lamb4() << endl; auto lamb5 = [=, &b](int c)->int {return b += a + c; }; //各部分完整的lambda表达式
cout << "lamb5(2) = "<<lamb5() << ", b = " << b<< endl; //9, 9 //2. lambda表达式的常量性及mutable关键字
a = ;
//auto f1 = [] {return a++; }; //error,没有捕获外部变量
//auto f2 = [=]() { a = 1;}; //error,const函数不能修改按值捕获的变量
auto f2 = [=]() mutable { a = ; }; //ok,被mutable修饰
auto f3 = [&a]() { a = ; }; //ok,按引用传递。const函数时影响引用本身,表示其不可修改
//但其引用的内容不受const影响,仍可修改。
//3. 捕获的时间点
int x = ;
auto lambByVal = [x] {return x + ; }; //按值捕获:声明时,x被复制一下
auto lambByRef = [&x] {return x + ; };//按引用捕获:x的值是随外部x的变化而变化。
cout << "lambByVal() = "<< lambByVal() << endl; //
cout << "lambByRef() = "<< lambByRef() << endl; // ++x; cout << "lambByVal() = " << lambByVal() << endl; //
cout << "lambByRef() = " << lambByRef() << endl; //12 //4. lambda表达式转换为函数指针
using FuncX = int(*)(int);
using FuncXY = int(*)(int, int); int k = ;
auto lambN = [](int x, int y) {return x + y; }; //无捕获列表
auto lambK = [&k](int x, int y) {return x + y + k; }; //有捕获列表 FuncXY funcXY;
funcXY = lambN; //ok,无捕获列表的lambda可转化为函数指针
//lambN = funcXY; //error,不能将函数指针转为lambda
//funcXY = lambK; //error,有捕获列表的lambda不能转为函数指针 //5. 捕获this指针(见Test类) return ;
}

初识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

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std; int main()
{
//1. 泛型局部函数
auto f1 = [](auto x, auto y) {return x + y; };
cout <<"f1(1, 3) = " << f1(, ) << endl;
cout <<"f1(4.5, 6.3) = " << f1(4.5, 6.3) << endl;
cout << "f1(\"abc\", \"def\") = " << f1(string{ "abc" }, "def") << endl; //2. 泛型回调函数
auto f2 = [](auto x) {cout << x << ","; };
vector<int> v1{, , };
vector<string> v2{ "a","b","c" };
for_each(v1.begin(), v1.end(), f2); cout << endl;
for_each(v2.begin(), v2.end(), f2); cout << endl; //3. 泛型lambda与函数指针的转换
auto f3 = [](auto x){ return x; };
using Func = int(*)(int);
Func pf = f3;
cout << pf() << endl; // cout << "----------------------------------"<< endl;
//4. 利用泛型lambda模拟实现tuple。
//(1)make_tuple接受可变参数包,返回一个tuple对象(lambda)。
// 如,auto tp = make_tuple(1, '2', "3");即tp = [](auto access){return access(1, '2', "3")};
// 可见参数包己被展开,并保存在tp中,可随时供访问器使用。
//(2)该tuple的使用方式是:tuple(access),向其传入某种功能的访问器(如打印,求长度函数)
// 这种通过tuple+access的方式可以达到访问tuple中元素的目的。注意,access为可接受可变参数包的
// 可调用对象。
auto make_tuple = [](auto ...xs) {
return [=](auto access) { return access(xs...); };
}; //4.1 求tuple的长度
auto length = [](auto xs) { //length(tp),传入tuple
return xs([](auto ...z) { return sizeof...(z); }); //通过tp(access)向其传入计算元素个数的访问器。
}; //4.2 fmap的功能: 将一个tuple(tpSrc)通过func函数映射成另一个新的tuple。如通过对元素*2,
//将tuple(1,2,3,4)映射成tuple(2,4,6,8)
auto fmap = [=](auto func) { //捕获make_tuple
return [=](auto tpSrc) { //捕获func和make_tuple
return tpSrc([=](auto... xs) { return make_tuple(func(xs)...); }); //tuple+访问器方式
};
}; auto tp = make_tuple(, '', "");
std::cout << length(tp) << std::endl; //
int len = tp([](auto...z) { return sizeof...(z); }); //通过向tuple传入访问器的方式来使用tuple。
//注意访问器必须可接受可变参数包。
std::cout << len << std::endl; auto twice = [](auto i) { return * i; }; //映射函数
auto print = [](auto i) { std::cout << i << " "; return i; };
auto tp1 = make_tuple(, , , );
auto tp2 = fmap(twice)(tp1); //将tp1通过twice函数映射成tp2
auto tp3 = fmap(print)(tp2); //将tp2通过print函数映射成tp3,并通过print将tp2元素打印出来。
//make_tuple(func(xs)...) 等价于make_tuple(func(xs1),func(xs2),...)
//因参数按从右向左依次传入,所以最终打印结果为8,6,4,1 return ;
}
/*输出结果
f1(1, 3) = 4
f1(4.5, 6.3) = 10.8
f1("abc", "def") = abcdef
1,2,3,
a,b,c,
5
----------------------------------
3
3
8 6 4 2
*/

二. lambda表达式的优势

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

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

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

【编程实验】lambda的优势

#include <iostream>
#include<algorithm>
#include <functional> using namespace std;
using namespace std::placeholders; int g_ubound = ;
vector<int> nums = { , , , , , , , , ,,,, };
vector<int> largeNums; //显示vector中的元素
void print(vector<int>& vec)
{
for (auto& elem : vec) {
cout << elem << " ";
} cout << endl;
} //函数
inline void LargNumsFunc(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);
}
}
};
void test(int ubound)
{
//1. 使用传统的for(缺点:需要直接使用全局变量g_ubound)
for (auto iter = nums.begin(); iter != nums.end(); ++iter) {
if (*iter > ubound) {
largeNums.push_back(*iter);
}
}
print(largeNums); largeNums.clear();
//2.使用函数指针
//缺点:函数定义在别的地方,代码阅读不方便。inline非强制性的,内联不一定成功。可
// 能导致性能问题。且LargeNumFunc由于使用了全局变量,是个有状态函数,函数重用性不高。
for_each(nums.begin(), nums.end(), LargNumsFunc);
print(largeNums); largeNums.clear();
//3. 使用仿函数
//优点:仿函数可以拥有状态,由于for_each第3个参数只能传递一个可调用对象而不能传递额外的参数。
// 因此,利用仿函数就可以克服这一不足。
//缺点:需要单独定义一个仿函数类。
for_each(nums.begin(), nums.end(), LargeNums(g_ubound));
print(largeNums); largeNums.clear();
//4. 使用lambda表达式
//优点:比仿函数书写上更简洁,代码的功能更清晰。
for_each(nums.begin(), nums.end(), [ubound](int i)
{
if (i > ubound) {
largeNums.push_back(i);
}
});
print(largeNums);
}
int main()
{
//1. lambda可简化标准库的调用(统计(50,73]之间的元素个数)
vector<int> v{ , , , , , , , };
//1.1 组合使用bind
auto f1 = std::bind(std::logical_and<bool>(),
std::bind(std::greater<int>(), _1, ),
std::bind(std::less_equal<int>(), _1, )
);
cout << count_if(v.begin(), v.end(), f1) << endl; //2 //1.2 使用lambda表达式
auto f2 = [](int x) {return (<x) && (x<=); };
cout << count_if(v.begin(), v.end(), f2) << endl; //
int cnt = count_if(v.begin(), v.end(), [](int x)
{
return ( < x) && (x <= );
});
cout << cnt << endl; //2 //2. lambda与其他可调用对象使用上的比较
test(g_ubound); return ;
}
/*输出结果
2
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
*/

第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. Quartz的配置与使用

    什么是Quartz Quartz是OpenSymphony开源组织在Job scheduling领域的开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用.Quartz可以用来创建简单或为 ...

  2. mysql快速安装教程详解

    Mysql 一.在https://www.cnblogs.com/zyx110/p/10799387.html中下载如下图中的压缩包 二.将mysql压缩包解压至c盘根目录下 三.把配置文件中的东西复 ...

  3. ELK学习笔记之Elasticsearch删除指定日期的数据(脚本+定时任务)

    0x00 概述 目前使用的ES版本为6.3.x,经过长时间的积累,ES内数据越来越多,需要删除指定日志之前的日志. 例如只要求保留60天的日志,那么就要删除60天之前的所有索引和日志. 0x01 ES ...

  4. linq,skip(),take实现分页

    using (AdventureWorks2012Entities db = new AdventureWorks2012Entities()) { int num = (from stu in db ...

  5. PhantomJS简单使用

    PhantomJS下载地址:   http://phantomjs.org/download.html 简单使用: from selenium import webdriver # 要想调用键盘按键操 ...

  6. SQLAlchemy多表操作

    目录 SQLAlchemy多表操作 一对多 数据准备 具体操作 多对多 数据准备 操作 其它 SQLAlchemy多表操作 一对多 数据准备 models.py from sqlalchemy.ext ...

  7. 大学外语四六级英语词汇CET

    anticipation n. 预期,期望 appreciation n. 感谢,感激 array n. 陈列,一系列 assurance n. 保证 emergency n. 紧急情况 encour ...

  8. Django ORM 一对多 和 多对多

    一对多 在 models.py 上定义: class Province(models.Model): name = models.CharField(max_length=32) def __str_ ...

  9. E05 【餐厅】What kind of coffee or tea would you like?

    核心句型 What  kind  of  coffee or tea would you like? 你想喝什么咖啡或者茶? What  would  you like? 你喜欢什么?/你想要什么? ...

  10. mysql性能优化随笔

    mysql性能优化是一个很大的命题,这里只记录一下近期的一些小经验. 曾经以为看了点create table时加index的语法就觉得自己知道怎么做mysql优化了,后来又看了点介绍mysql索引底层 ...