C++11的更新介绍(lamada、包装器)

🪐🪐🪐欢迎来到程序员餐厅💫💫💫
主厨:邪王真眼
主厨的主页:Chef‘s blog
所属专栏:c++大冒险
总有光环在陨落,总有新星在闪烁
lambda表达式
C++98中的一个例子
在C++98中,如果想要进行排序,可以使用std::sort方法如果待排序元素为自定义类型,需要用户定义排序时的比较规则:
struct Goods
{string _name; // 名字double _price; // 价格int _evaluate; // 评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};
struct ComparePriceLess
{bool operator()(const Goods& gl, const Goods& gr){return gl._price < gr._price;}
};
struct ComparePriceGreater
{bool operator()(const Goods& gl, const Goods& gr){return gl._price > gr._price;}
};
int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
3 }, { "菠萝", 1.5, 4 } };sort(v.begin(), v.end(), ComparePriceLess());
sort(v.begin(), v.end(), ComparePriceGreater());
}
随着C++语法的发展,人们开始觉得上面的写法太复杂了,每次为了实现一个algorithm算法,
都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,
这些都给编程者带来了极大的不便。因此,在C++11语法中出现了Lambda表达式。
lambda表达式语法
-
lambda表达式书写格式:
[capture-list] (parameters) mutable -> return-type { statement }
- [capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来 判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。
- (parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略
- mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
- ->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。
- {statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获 到的变量。
-
格式省略情况:
1.mutable可省略
int a = 0, b = 0;
auto func = [a, b]()->int {return 0; };
2.返回值类型可省略,编译器自动推导
int a = 0, b = 0;
auto func = [a, b]() {return 0; };
3.没有传参时列表可省略
int a = 0, b = 0;
auto func = [a, b] {return 0; };
4.而捕捉列表和函数体可以为空。
// 省略了返回值类型,无返回值类型auto fun1 = [](int c){ };
因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情。
lambda表达式返回值实际上是仿函数,
该仿函数无法直接调用,如果想要调用,可借助auto将其赋值给一个变量,lambda返回的仿函数对象,其类名是随机的,因此必须用auto来接受这个仿函数对象。
捕获列表说明
捕捉列表描述了父作用域中那些数据可以被lambda使用,以及使用的方式传值还是传引用。
1.[var]:表示值传递方式捕捉变量var
int a = 0, b = 0;
auto func = [a, b] (){return a+b};
注意:此时a,b具有常性,如果要去掉他们的常性就要加上mutable,此时lambda获得ab的方式是传值,所以不会影响到父作用域的ab
int a = 0, b = 0;
auto func = [a, b] ()mutable{return a+b};
2.[this]:表示值传递方式捕捉当前的this指针
3.[=]:表示值传递方式捕获所有父作用域中的变量(包括this)
int a = 0, b = 0;
auto func = [=]()mutable {return a + b; };
4.[&var]:表示引用传递捕捉变量var
此时lambda获得ab的方式是传引用,在lambda里修改ab会影响父作用域的ab
int a = 0, b = 0;
auto func = [&a,&b]{return a + b; };
5.[&]:表示引用传递捕捉所有父作用域中的变量(包括this)
int a = 0, b = 0;
auto func = [&]() {return a + b; };
6.我们还可以把传值和传引用混合使用,让部分参数传参,部分参数传引用
[x, &y]:以传值的形式捕获x,以传引用的形式捕获y
[=, &x]:以传值的形式捕获父作用域所有变量,以传引用的形式捕获x
[&, x]:以传值的形式捕获x,以传引用的形式捕获父作用域所有变量
注意:
- 父作用域指包含lambda函数的语句块
- c捕捉列表不允许变量重复传递,否则就会导致编译错误。 比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复
- 在块作用域以外的lambda函数捕捉列表必须为空。
- 在块作用域中的lambda函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者非局部变量都会导致编译报错。
- lambda表达式之间不能相互赋值,因为他们的实际类名不同(每一个类名都是lamdba随机生成的)
有lambda后,我们在需要仿函数的地方,就无需额外写一个仿函数的类,而是直接写lambda表达式:
int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
3 } };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表达式
函数对象,又称为仿函数,即可以想函数一样使用的对象,就是在类中重载了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表达式完全一样。
函数对象将rate作为其成员变量,在定义对象时给出初始值即可,lambda表达式通过捕获列表可
以直接将该变量捕获到。
实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如
果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator(),而这个类的名字是编译器随机产生的,所以lambda表达式之间不能相互赋值,即使看起来类型相同,因为他们的类名是不同的
模板参数中的lambda表达式
我们要给set传一个仿函数
set<int, Less<int>>;
请注意,这里我们所传的不是对象,而是类,但是lambda返回值本身就是对象,所以直接传lambda肯定是不可以的
这个时候decltype就登场了
auto func = [](int a, int b) {return a < b; };
set<int, decltype(func)>;
包装器
function包装器 也叫作适配器。C++中的function本质是一个类模板
为什么需要function
如果一个变量f,可以按f()的形式调用函数,那么称f是一个可调用对象
基于此不难想到可调用对象包括:函数、仿函数、lambda
我们来看看他们在下面代码的表现
template<class F, class T>
T useF(F f, T x)
{static int count = 0;cout << "count:" << ++count << endl;cout << "count:" << &count << endl;return f(x);
}
double f(double i)
{return i / 2;
}
struct Functor
{double operator()(double d){return d / 3;}
};
int main()
{// 函数cout << useF(f, 11.11) << endl;// 函数对象cout << useF(Functor(), 11.11) << endl;// lamber表达式cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;return 0;
}
通过上面的程序验证,我们会发现useF函数模板实例化了三份。
然而,这里的函数、仿函数、lambda的返回值和参数类型相同,可以认为三者极其相似,有没有办法让函数模板把他们识别为一种类型,从而只需要实例化一份呢
包装器可以很好的解决上面的问题,function包含在头文件<functional>中,是一个类模板,模板原型如下:
template <class T> function;template <class Ret, class... Args>
class function<Ret(Args...)>;
其语法为:function<返回值(参数列表)>,只要是返回值和参数列表相同的可调用对象,经过这一层封装,都会变成相同的类型。
int f(int a, int b)
{return a + b;
}
struct Functor
{
public:int operator() (int a, int b){return a + b;}
};
template<class F>
void Function(F f)
{static int count = 0;cout << count++<<endl;cout << &count << endl;cout << "================="<<endl;
}
int main()
{// 函数名(函数指针)function<int(int, int)> func1 = f;Function(func1);// 函数对象function<int(int, int)> func2 = Functor();Function(func1);// lamber表达式function<int(int, int)> func3 = [](const int a, const int b){return a + b; };Function(func1);
}
可以看出func1、func2、func3被认为是一种类型
function接收对象成员函数
class Plus
{
public:static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return a + b;}
};
int main()
{// 类的成员函数function<int(int, int)> func4 = &Plus::plusi;function<double(Plus*, double, double)> func5 = &Plus::plusd;
function<double(Plus, double, double)> func5 = &Plus::plusd;
}
注意事项:
- 等号右边的&符号别忘了写,对于非静态函数必需加上,非静态最好加上
- 等号右边的函数要指定类域
- 对于非静态函数,左边的第一个参数是类名或类指针
适用场景:
比如力扣:波兰表达式
对于相邻两数我们要以case语句对+-*/进行讨论
class Solution {
public:
int evalRPN(vector<string>& tokens) {stack<int> st;for(auto& str : tokens){if(str == "+" || str == "-" || str == "*" || str == "/"){int right = st.top();st.pop();int left = st.top();st.pop();switch(str[0]){case '+':st.push(left+right);break;case '-':st.push(left-right);break;case '*':st.push(left*right);break;case '/':st.push(left/right);break;}}//..........}return st.top();
}
};
使用包装器以后的玩法
class Solution {
public:
int evalRPN(vector<string>& tokens) {stack<int> st;map<string, function<int(int, int)>> opFuncMap ={{ "+", [](int i, int j){return i + j; } },{ "-", [](int i, int j){return i - j; } },{ "*", [](int i, int j){return i * j; } },{ "/", [](int i, int j){return i / j; } }};for(auto& str : tokens){if(opFuncMap.find(str) != opFuncMap.end()){int right = st.top();st.pop();int left = st.top();st.pop();st.push(opFuncMap[str](left, right));}//........}return st.top();
}
};
冷知识:
-
function 类型相同的对象可以相互赋值
function<int(int)> f1 = [](int x) { return x * x; };
function<int(int)> f2 = f1; // f2 现在也是一个 lambda 表达式
-
function实现了对bool的重载
opearotr bool函数重载方式如下
class A
{
public:operator bool(){return 3==_a;}int _a = 2;
};int main()
{A a;if (a)cout << "666" << endl;else{cout << "888" << endl;}
}
function 对象支持 bool 类型转换,可以用于判断 function 对象是否为空(未初始化)。
function<int(int)> f;
if (!f) {cout << "f is empty" << endl;
}
bind
bind音译即是绑定,它是C++标准库中的一个函数模板,用于将函数与其参数进行绑定,生成一个新的可调用对象。通过bind,我们可以将函数的部分参数固定下来,也可以交换参数位置,从而得到一个新的函数对象。
// 原型如下:
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2)
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
调用bind的一般形式
auto newCallable = bind(callable,arg_list);
newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的callable的参数。当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数。
-
交换参数位置
交换了第一个参数和第二个参数的位置
C++11后新增一个命名空间域placeholders,其内部会存储很多变量,这些变量用于函数的传参
placeholders::_n表示原函数中的第n个参数
#include <functional>
void Mod(int a, int b)
{cout << a % b<<endl;
}
int main()
{std::function<void(int, int)> func1 = std::bind(Mod, placeholders::_2,placeholders::_1);func1(2, 1);Mod(2, 1);return 0;
}
-
给参数绑定固定值
void Mod(int a, int b)
{cout << a % b<<endl;
}
int main()
{std::function<void(int,int)> func1 = std::bind(Mod, 1, placeholders::_2);func1(2, 10);Mod(1, 10);return 0;
}
可以看出即使我们在func1中给第一个参数传参为2,实际也依旧是1,
三种写法:
function<void(int,int)> func1 = std::bind(Mod, 1, placeholders::_1);
func1(1,10);
function<void(int)> func2 = std::bind(Mod, 1, placeholders::_1);
func2(10);
auto func3 = std::bind(Mod, 1, placeholders::_1);
func3(1, 10);
func3(10);
我建议是auto,它的适用范围最大
🥰创作不易,你的支持对我最大的鼓励🥰
🪐~ 点赞收藏+关注 ~🪐

相关文章:
C++11的更新介绍(lamada、包装器)
🪐🪐🪐欢迎来到程序员餐厅💫💫💫 主厨:邪王真眼 主厨的主页:Chef‘s blog 所属专栏:c大冒险 总有光环在陨落,总有新星在闪烁 lambda表达式 C98中的一个…...
Golang 实现一个简单的 RPC 服务
分享一个简单的 rpc 服务框架 一、服务端实现 package mainimport ("log""net""net/rpc" )const HelloServiceName "main.HelloService"type HelloServiceInterface interface {Hello(request string, replay *string) error }func…...
Linux系统(centos,redhat,龙芯,麒麟等)忘记密码,怎么设置新的密码
Linux系统(centos,redhat,龙芯,麒麟等)忘记密码,怎么设置新的密码 今天在操作服务器时,DBA忘记了人大金仓数据库的kingbase密码,他的密码试了好多遍,都不行。最后只能给重置密码了 解决办法&a…...
SpringBoot的启动原理
运行Main方法: 应用程序启动始于Main方法的执行。在Main方法中,创建了一个SpringApplication实例,用于引导应用程序的启动。同时,SpringApplication会根据spring.factories文件加载并注册监听器、ApplicationContextInitializer等…...
git查看单独某一个文件的历史修改记录
git查看单独某一个文件的历史修改记录 git log -p 文件具体路径 注意,Windows下默认文件路径分隔符是 \,在git bash 里面需要改成 /。 git基于change代码修改与提交_git change-CSDN博客文章浏览阅读361次。git cherry-pick:复制多个提交comm…...
一键开启Scrum回顾会议的精彩时刻
其实回顾会议作为一个检视、反馈、改进环节,不仅在传统的瀑布管理模式中,还是在Scrum一类的敏捷管理流程中,都是非常重要的活动。一些团队认为它无法产生直接的价值,所以有意忽略了这个会议;一些团队在越来越多的回顾中…...
Python计算多个表格中多列数据的平均值与标准差并导出为新的Excel文件
本文介绍基于Python语言,对一个或多个表格文件中多列数据分别计算平均值与标准差,随后将多列数据对应的这2个数据结果导出为新的表格文件的方法。 首先,来看一下本文的需求。现有2个.csv格式的表格文件,其每1列表示1个变量&#x…...
nginx支持的多种负载均衡策略
目录 1.轮询(默认) 2. ip_hash 3. 加权轮询(weight) 4. fair(第三方) 5. 最少连接(least_conn) 1.轮询(默认) 将请求依次分配给每个服务器,确…...
FNP preptool has not been run on this executable
pycharm导入arcgis pro的python运行程序后提示 我也看了很多解决方法,也重新安装过一遍,终于看到一个说法是你的arcgis pro是学习版所以才会有这个提示,不会影响输入 测试代码 import arcpy print(arcpy.GetInstallInfo()[Version])输出结果…...
算法-反转单向链表
需求 思路 链表必有节点,节点两要素:当前元素值,下一个节点地址 import java.util.Scanner;// 定义一个单向链表 public class MyLinkedList<E> {int size 0;// 顶一个私有的内部类,表示链表的节点public class Node {E da…...
Ps 滤镜:方框模糊
Ps菜单:滤镜/模糊/方框模糊 Filter/Blur/Box Blur 方框模糊 Box Blur滤镜通过计算图像中每个像素及其周围像素的平均颜色值来实现模糊效果。适合于需要突出主题、减少背景(尤其是颜色变化)干扰的场景。 “方框模糊”滤镜按照设定的半径值&…...
MTK Android13 霸屏实现
一、背景 在台式POS场景下,经常有应用会需要获取霸屏的权限,隐藏状态栏或者导航栏,且不能被划出,其实系统已经系统了隐藏状态栏也导航栏的接口,但是无法做到禁止滑出。 View decorView ((Activity) context).getWin…...
PyTorch神经网络打印存储所有权重+激活值(运行时中间值)
很多时候嵌入式或者新硬件需要纯净的权重模型和激活值(运行时中间值),本文提供一种最简洁的方法。 假设已经有模型model和pt文件了,在当前目录下新建weights文件夹,运行这段代码,就可以得到模型的权重&…...
grpc-教程(golang版)
目录 一、介绍 二、环境准备 三、Golang中使用grpc 1.编写protobuf文件 2.服务端 3.客户端 四、proto文件详解 1.proto语法 2.数据类型 基本数据类型 数组类型 map类型 嵌套类型 编写风格 3.多服务 4.多个proto文件 五、流式传输 1.普通rpc 2.服务器流式 …...
Spring与Spring Boot的区别:从框架设计到应用开发
这是我自己开发的一款小程序,感兴趣的可以体验一下: 进入正题: 在Java开发领域,Spring和Spring Boot都是备受推崇的框架,它们为开发人员提供了丰富的功能和便捷的开发体验。然而,许多人对它们之间的区别仍…...
React Hooks 全解: 常用 Hooks 及使用场景详解
React Hooks 是 React 16.8 版本引入的一项重要特性,它极大地简化和优化了函数组件的开发过程。 React 中常用的 10 个 Hooks,包括 useState、useEffect、useContext、useReducer、useCallback、useMemo、useRef、useLayoutEffect、useImperativeHandle 和 useDebugValue。这些…...
第十三届蓝桥杯真题:x进制减法,数组切分,gcd,青蛙过河
目录 x进制减法 数组切分 gcd 青蛙过河 x进制减法 其实就是一道观察规律的题。你发现如果a这个位置上的数x,b这个位置上的数是y,那么此位置至少是max(x,y)1进制。一定要把位置找对啊 #include <bits/stdc.h> using namespace std; typedef l…...
JavaEE初阶Day 6:多线程(4)
目录 Day 6:多线程(4)1. 线程不安全的原因2. 锁3. synchronized Day 6:多线程(4) 前序:针对Day 5结尾的count 多线程的执行,是随机调度抢占式的执行模式,某个线程执行指…...
微信小程序 django+nodejs电影院票务售票选座系统324kd
小程序Android端运行软件 微信开发者工具/hbuiderx uni-app框架:使用Vue.js开发跨平台应用的前端框架,编写一套代码,可编译到Android、小程序等平台。 前端:HTML5,CSS3 VUE 后端:java(springbootssm)/python(flaskdja…...
基于springboot实现桂林旅游景点导游平台管理系统【项目源码+论文说明】计算机毕业设计
基于springboot实现桂林旅游景点导游平台管理系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了桂林旅游景点导游平台的开发全过程。通过分析桂林旅游景点导游平台管理的不足,创建了一个计算…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
