【C++11(二)】lambda表达式和可变参数模板

在这里插入图片描述

一、可变参数模板

C++11的新特性可变参数模板
能够让您创建可以接受
可变参数的函数模板和类模板

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

参数包是不支持下标解析的
在这里插入图片描述

1.1 递归函数方式展开参数包

// 递归终止函数
void ShowList()
{cout << endl;
}// 展开函数
// 增加一个模板参数让编译器自己去解析这个参数包里的东西
template <class T, class ...Args>
void ShowList(const T& value, Args... args)
{cout << value <<" ";ShowList(args...); // 如果是0个参数的参数包就会调用void ShowList()// 如果参数包接收的char类型的参数,会再去调匹配的ShowList函数// 调不到就只能调自己,根据模板推导类型// 打印完,参数包再往下传,0个参数就调用void ShowList()// void ShowList()可以认为是递归终止函数
}int main()
{ShowList(); // 编译器会找最匹配的,调void ShowList()ShowList(1); // 1传给value,后面的参数包就没有了,参数包代表0-n个参数ShowList(1, 'A'); // 1传给value,'A'传给参数包ShowList(1, 'A', std::string("sort"));return 0;
}

二、lambda表达式

C++98中
对一个数据集合中的元素排序
可以使用std::sort

如果待排序元素为自定义类型
需要自己写仿函数
如果每次按自定义类型不同的成员变量
进行排序就要写多个仿函数,十分不方便
因此,C++11语法中的Lambda表达式
便是解决此类问题

lambda表达式

int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠萝", 1.5, 4 } };sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){return g1._price < g2._price; });sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){return g1._price > g2._price; });sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){return g1._evaluate < g2._evaluate; });sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){return g1._evaluate > g2._evaluate; });
}

可以发现lambda表达式是一个匿名函数

1.1 lambda表达式语法

书写格式:
[捕捉列表] (参数列表) mutable ->return-type { 函数体 }

  • 捕捉列表:该列表总出现lambda函数开始位置
    编译器根据[]判断接下来的代码是否为lambda函数
    捕捉列表能够捕捉上下文中的变量供lambda函数使用
  • 参数列表:与普通函数的参数列表一致
    如果不需要参数传递,则可以连同()一起省略
  • mutable:默认情况lambda函数总是const函数,mutable可以取消其常量性
    使用该修饰符参数列表不可省略(即使参数为空)
  • ->return-type:返回值类型
    用追踪返回类型形式声明函数的返回值类型
    无返回值可略,有返回值也可略,由编译器推导
  • { 函数体 }:该函数体内除了可使用其参数外
    还可以使用所有捕获到的变量

用lambda表达式实现add

auto add = [](int x, int y)->int { return x + y; };// cout << [](int x, int y)->int { return x + y; }(1, 2) << endl; // 这样写比较抽象
cout << add(1, 2) << endl;auto add2 = [](int x, int y)
{ return x + y; 
};
cout << add2(3, 2) << endl;[] {}; // 最简单的lambda,捕捉列表和函数体是一定不能省略的

用lambda对自定义类型比较大小

struct Goods
{string _name; // 名字double _price; // 价格int _evaluate; // 评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠萝", 1.5, 4 } };// 用lambda对自定义类型比较大小// <auto priceLess = [](const Goods& g1, const Goods& g2)->bool { return g1._price < g2._price; };sort(v.begin(), v.end(), priceLess);// >sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool { return g1._price > g2._price; });// 比较评价sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool {return g1._evaluate > g2._evaluate; });sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool {return g1._evaluate < g2._evaluate; });return 0;
}

捕捉变量

int main()
{int x = 1, y = 2;auto swap1 = [](int& rx, int& ry){// 只能用当前作用域的变量int tmp = rx;rx = ry;ry = tmp;};swap1(x, y);cout << x << " " << y << endl;// 还可以换一种写法// 想用外面的变量,则可以利用捕捉列表进行捕捉(捕捉过来的对象是外面对象的拷贝)/*// 传值捕捉auto swap2 = [x, y]() mutable // 捕捉多个值用逗号分割即可;直接给值叫做传值捕捉,传值捕捉无法修改,加上mutable(异变)就可以修改{int tmp = x;x = y;y = tmp;};swap2();cout << x << " " << y << endl;*/// mutable用得比较少,建议// 引用捕捉auto swap2 = [&x, &y](){int tmp = x;x = y;y = tmp;};swap2();cout << x << " " << y << endl;// 还可以混合捕捉,x引用捕捉,y传值捕捉// 全部引用捕捉auto func1 = [&](){// ...};// 全部传值捕捉auto func2 = [=](){// ...};// 全部引用捕捉,x传值捕捉auto func3 = [&, x](){// ...};return 0;
}

1.2 函数对象与lambda表达式

函数对象,又称为仿函数
即可像函数一样使用的对象
(在类中重载了operator()运算符的类对象)

class Rate
{
public:Rate(double rate): _rate(rate){}double operator()(double money, int year){ return money * _rate * year;}
private:double _rate;
};
int main()
{
// 函数对象double rate = 0.49;Rate r1(rate);r1(10000, 2);
// lamberauto r2 = [=](double monty, int year)->double{return monty*rate*year; 
};r2(10000, 2);return 0;
}

lambda表达式大小为1个字节
在编译器角度是没有lambda
定义一个lambda
编译器自动生成一个仿函数对象的类型
在该类中重载了operator()
该类是一个空类,空类没给成员变量就是一个字节

函数对象将rate作为其成员变量
在定义对象时给出初始值即可
lambda表达式通过捕获列表可以
直接将该变量捕获到

三、包装器

C++中的function本质是一个类模板
也是一个包装器

int f(int a, int b)
{return a + b;
}struct Functor
{
public:int operator()(int a, int b){return a + b;}
};int main()
{// int(*pf1)(int, int) = f; // 不是常规的指针类型,写法复杂// 假设要求要声明一个统一的类型// map<string, > // 这里要声明可调用类型,f和Functor调用起来都是一样的,但类型不一样,一个是函数指针一个是类// 这时候就没法声明,而包装器就可以统一封装出可调用类型function<int(int, int)> f1 = f; // 返回值加参数包,参数包就是把实际要的类型写上function<int(int, int)> f2 = Functor(); // Function可以对函数指针和仿函数对象进行包装function<int(int, int)> f3 = [](int a, int b) { return a + b; };cout << f1(1, 2) << endl; // 包装以后两个类型的对象是一样的cout << f2(2, 2) << endl;cout << f3(3, 3) << endl;map<string, function<int(int, int)>> opFuncMap; opFuncMap["函数指针"] = f;opFuncMap["仿函数"] = Functor();opFuncMap["lambda"] = [](int a, int b) { return a + b; };// 包装器的作用:更好的控制可调用对象的类型return 0;
}

静态成员和非静态成员的包装

class Plus
{
public:Plus(int rate): _rate(rate){}static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return (a + b) * _rate;}
private:int _rate = 2;
};int main()
{function<int(int, int)> f1 = Plus::plusi; // 包装静态成员函数正常包装就可以// 非静态成员函数,就不能这样直接包装// function<double(double, double)> f2 = Plus::plusd;// error C3867: “Plus::plusd”: 非标准语法;请使用 "&" 来创建指向成员的指针// 普通成员函数名代表函数指针.静态成员也一样,指定类域就可取到这个函数指针// 非静态成员需要加一个&// 非静态传参还有一个this指针需要传参// error C2440 : “初始化”: 无法从“double(__cdecl Plus::*)(double, double)”转换为“std::function<double(double, double)>”function<double(Plus, double, double)> f2 = &Plus::plusd; // 也可以传Plus*,左值能被取地址,右值不行,匿名对象是右值不能取地址,就不能用匿名对象// 传指针也可以,传对象也可以,因为这不是直接去掉用plusd这个函数,我是一个包装器,f1是直接调用Plusi// f2是用对象去掉用Plusd// 当Plusd是指针,就用指针取调用Plusd// 如果是对象就用对象调用Plusd cout << f1(1, 2) << endl;cout << f2(Plus(2), 20, 20) << endl; // 第一个正常调用,第二个需要加一个匿名对象;需要写一个构造函数,也可以不写,Plus就不能传参// 也可以不用匿名对象Plus p1(3);cout << f2(p1, 20, 20) << endl;return 0;
}

bind

调整参数顺序

void Print(int a, int b)
{cout << a << " ";cout << b << endl;
}int main()
{Print(10, 20); // auto RPrint =  bind(Print, placeholders::_2, placeholders::_1); // 第一个参数传可调用对象,_1是一个占位符也是第一个参数,-2是第二个参数以此类推,默认是拿不到的,它放在placeholders命名空间里面function<void(int, int)> RPrint = bind(Print, placeholders::_2, placeholders::_1);// 两种写法都可以推荐用autoRPrint(10, 20);return 0;
}

调整参数个数

class Sub
{
public:Sub(int rate): _rate(rate){}int func(int a, int b){return (a - b) * _rate;}
private:int _rate;
};int main()
{function<int(Sub, int, int)> fSub = &Sub::func;cout << fSub(Sub(3), 10, 20) << endl;function<int(int, int)> fSub1 = bind(&Sub::func, Sub(3), placeholders::_1, placeholders::_2);cout << fSub1(10, 20) << endl;// 把隐藏this指针绑死就只用传两个参数// 把第二个参数绑死function<int(Sub, int)> fSub2 = bind(&Sub::func, placeholders::_1, 100, placeholders::_2);// 第二个参数绑死了,第三个参数是_2,还是按顺序挨着走 cout << fSub2(Sub(3), 20) << endl;}

在这里插入图片描述
本篇博客完,感谢阅读🌹
如有错误之处可评论指出
博主会耐心听取每条意见

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/363988.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智慧记账,轻松管理,让借还款明细一目了然,一键导出

在繁忙的生活中&#xff0c;财务记账管理往往成为我们的一大难题。尤其是面对频繁的借还款项&#xff0c;如何快速、准确地记录每一笔收支明细&#xff0c;并确保数据的清晰、完整&#xff0c;成为许多人关注的焦点。现在&#xff0c;我们为您带来一款全新的记账管理工具——晨…

【第三方JSON库】org.json.simple用法初探—Java编程【Eclipse平台】【不使用项目管理工具】【不添加依赖解析】

本文将重点介绍&#xff0c;在不使用项目管理工具&#xff0c;不添加依赖解析情况下&#xff0c;【第三方库】JSON.simple库在Java编程的应用。 JSON.simple是一种由纯java开发的开源JSON库&#xff0c;包含在JSON.simple.jar中。它提供了一种简单的方式来处理JSON数据和以JSO…

有趣的仿神经猫html5圈小猫游戏源码

有趣的仿神经猫html5圈小猫游戏源码,点击小圆点&#xff0c;围住小猫游戏。猫已经跑到地图边缘&#xff0c;你输了。内含json数据&#xff0c;部署到服务器方可运行 微信扫码免费获取源码

Kafka 位移

Consumer位移管理机制 将Consumer的位移数据作为一条条普通的Kafka消息&#xff0c;提交到__consumer_offsets中。可以这么说&#xff0c;__consumer_offsets的主要作用是保存Kafka消费者的位移信息。使用Kafka主题来保存位移。 消息格式 位移主题就是普通的Kafka主题。也是…

Type-C接口OTG转接器的应用与发展

随着科技的飞速发展&#xff0c;智能移动设备已成为我们生活中不可或缺的一部分。而在这些设备的连接与数据传输中&#xff0c;Type-C接口以其高效、便捷的特性逐渐占据了主导地位。OTG&#xff08;On-The-Go&#xff09;技术则进一步扩展了Type-C接口的功能&#xff0c;使得设…

JavaSE主要内容(全套超完整)

一、为什么选择Java&#xff08;Java的优势&#xff09; 1、应用面广&#xff1a; 相较于其他语言&#xff0c;Java的应用面可谓是非常广&#xff0c;这得益于他的跨平台性和其性能的稳定性。他在服务器后端&#xff0c;Android应用开发&#xff0c;大数据开发&#xf…

鼠尾草(洋苏草)

鼠尾草&#xff08;Salvia japonica Thunb.&#xff09;&#xff0c;又名洋苏草、普通鼠尾草、庭院鼠尾草&#xff0c;属于唇形科鼠尾草属多年生草本植物。鼠尾草以其独特的蓝紫色花序和长而细密的叶片为特点&#xff0c;常用于花坛、庭院和药用植物栽培。 鼠尾草的名字源自于…

25考研:今年初试时间比去年更早了?

过去5年考研初试时间安排如下&#xff1a; 24考研&#xff1a;2023年12月23-24日&#xff08;倒数第二个周末&#xff09; 23考研&#xff1a;2022年12月24-25日&#xff08;倒数第二个周末&#xff09; 22考研&#xff1a;2021年12月25-26日&#xff08;最后一个周末&#xf…

Al+医学,用这个中文多模态医学大模型帮你看胸片

随着人工智能技术的飞速发展&#xff0c;AI 在医学领域的应用已经成为现实。特别是在医学影像诊断方面&#xff0c;AI 大模型技术展现出了巨大的潜力和价值&#xff0c;但目前针对中文领域医学大多模态大模型还较少。 今天马建仓为大家介绍的这款 XrayGLM&#xff0c;就是由澳…

浅谈安科瑞ACRELCLOUD-1200光伏发电系统在建筑节能中的应用

摘要&#xff1a;21世纪以来&#xff0c;随着不可再生能源的逐渐减少&#xff0c;人们越来越重视能源的利用率&#xff0c;不断开发绿色能源。通过光伏发电系统&#xff0c;能够提升能源利用率&#xff0c;减少不可再生能源的开发。同时&#xff0c;也能加强我国建筑节能系统的…

wsl2收缩虚拟磁盘,减少空间占用

一、说明 由于WSL2使用的是虚拟磁盘&#xff0c;当虚拟磁盘的空间变大时&#xff0c;仅仅删除WSL2文件系统中没有用到的大文件&#xff0c;磁盘空间是无法自动收缩回收的。本文介绍了一种回收WSL2虚拟磁盘空间的方法。 二、停止WSL2 在收缩 WSL2 虚拟磁盘之前&#xff0c;需…

Cent0S7 Docker安装 YOLOv8

githup 源码及其作者&#xff1a;ultralytics/ultralytics&#xff1a;新增 - PyTorch 中的 YOLOv8 &#x1f680; > ONNX > OpenVINO > CoreML > TFLite (github.com) yolo是什么&#xff1f; 实时视觉检测技术&#xff0c;通过对不同的角度拍摄的视觉图片进行人…

实现自动化:如何利用阿里云OSS上传文件并自动打标签

在当前数字化时代&#xff0c;数据管理变得愈发重要&#xff0c;特别是对于需要大规模存储和管理文件的场景。阿里云对象存储服务&#xff08;OSS&#xff09;作为业界领先的解决方案&#xff0c;不仅提供了稳定可靠的云存储&#xff0c;还支持丰富的扩展功能&#xff0c;如文件…

DNF手游鬼剑士攻略:全面解析流光星陨刀的获取与升级!云手机强力辅助!

《地下城与勇士》&#xff08;DNF&#xff09;手游是一款广受欢迎的多人在线角色扮演游戏&#xff0c;其中鬼剑士作为一个经典职业&#xff0c;因其强大的输出能力和炫酷的技能特效&#xff0c;吸引了众多玩家的青睐。在这篇攻略中&#xff0c;我们将详细介绍鬼剑士的一把重要武…

【Flink metric(1)】Flink指标系统的系统性知识:获取metric以及注册自己的metric

文章目录 一. Registering metrics&#xff1a;向flink注册新自己的metrics1. 注册metrics2. Metric types:指标类型2.1. Counter2.2. Gauge2.3. Histogram(ing)2.4. Meter 二. Scope:指标作用域1. User Scope2. System Scope ing3. User Variables 三. Reporter ing四. System…

基于AT89C52单片机的超声波测距设计—数码管显示

点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/89456475?spm=1001.2014.3001.5503 C 源码+仿真图+毕业设计+实物制作步骤+10 在这里插入图片描述 题 目: 基于52的超声波测距汽车防撞系统 学生姓名 [姓名] 学 号 [学号…

接口自动化测试关联token的方法?

引言&#xff1a; 在接口自动化测试中&#xff0c;有时候我们需要关联token来进行身份验证或权限管理。本文将从零开始&#xff0c;介绍如何详细且规范地实现接口自动化测试中token的关联。 步骤一&#xff1a;准备工作 在开始之前&#xff0c;我们需要确保以下准备工作已完成…

【股指期权投教】一手股指期权大概多少钱?

一手股指期权的权利金大概在几千人民币左右&#xff0c;如果是作为期权卖方还需要另外缴纳保证金的。国内的股指期权有三种&#xff0c;沪深300、上证50、中证1000股指期权&#xff0c;每点合约人民币100 元。 期权合约的价值计算可以通过此公式得出&#xff1a;权利金的支付或…

excel实现下拉筛选(超简单)

excel实现下拉筛选 引言1、需求&#xff1a;预警状态下的列 实现下拉筛选2、实现2.1、数据验证2.2、下拉筛选内容2.3、去掉预警状态单元格的下拉筛选 引言 通常&#xff0c;我们会单独新建一张sheet表 专门存每个列的下拉内容。下面我将专门建立一张名为代码表的sheet表来存放…

1/7精确到100位,1000位,100000位怎么算?

双精度 Console.WriteLine("1/7的值是" (double)1 / 7);结果:0.14285714285714285 即使使用双精度浮点数&#xff0c;精确的位数也是有限的&#xff0c;如果想精确到小数点后100位&#xff0c;1000位&#xff0c;甚至更高哪&#xff1f; 朴素的除法 除数 余数 商…