C++11——包装器与lambda表达式

目录

一.背景

二.lambda

1.见一见lambda

2.lambda表达式语法

3.lambda捕捉列表说明

三.函数对象与lambda表达式

四.包装器

1.function包装器

2.包装类的成员函数

 五.bind

1.调整参数位置

2.减少函数参数


一.背景

在C++98中,如果想要对一个数据集合中的元素进行排序,可以使用std::sort方法。

#include<algorithm>
#include<vector>
using namespace std;void Print(vector<int>& arr)
{for (auto e : arr){cout << e << " ";}cout << endl;
}int main()
{vector<int> arr = { 4,1,8,5,3,7,0,9,2,6 };//降序sort(arr.begin(), arr.end(),greater<int>());Print(arr);//升序sort(arr.begin(), arr.end());Print(arr);return 0;
}

如果待排序元素为自定义类型,需要用户定义排序时的比较规则:

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;}
};void Print(vector<Goods>& v)
{for (auto e : v){cout << e._name << ":" << e._price << ":" << e._evaluate << endl;}cout << endl;
}
int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };sort(v.begin(), v.end(), ComparePriceLess());Print(v);sort(v.begin(), v.end(), ComparePriceGreater());Print(v);return 0;
}

随着C++语法的发展,人们开始觉得上面的写法太复杂了,每次为了实现一个algorithm算法,
都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,
这些都给编程者带来了极大的不便。
因此,在C++11语法中出现了Lambda表达式。

二.lambda

1.见一见lambda

改装上面的代码:

struct Goods
{string _name;  //名字double _price; //价格int _evaluate; //评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};void Print(vector<Goods>& v)
{for (auto e : v){cout << e._name << ":" << e._price << ":" << e._evaluate << endl;}cout << endl;
}
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)->bool {return g1._price < g2._price; });Print(v);sort(v.begin(), v.end(), [](const Goods g1, const Goods g2)->bool {return g1._price > g2._price; });Print(v);return 0;
}

上述代码就是使用C++11中的lambda表达式来解决,可以看出lambda表达式实际是一个匿名函
数。

2.lambda表达式语法

lambda表达式书写格式:[capture-list] (parameters) mutable -> return-type { statement }

[capture-list] :

捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。

int main()
{int a = 100;int b = 200;int sum = 0;auto lam = [a, b]()->int {return a + b; };sum = lam();cout << "a + b = "<<sum << endl;return 0;
}

(parameters):

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

int main()
{int a = 100;int b = 200;int sum = 0;auto lam = [](int a,int b)->int {return a + b; };sum = lam(a,b);cout << "a + b = "<<sum << endl;return 0;
}

mutable:

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

 默认对捕捉的变量有const属性:

int main()
{int a = 100;int b = 200;int sum = 0;auto lam = [a,b](){a = b = 50;cout << "a:" << a << endl;cout << "b:" << b << endl;};lam();cout << "a:" << a << endl;cout << "b:" << b << endl;return 0;
}

 加上mutable关键字:

注意:

  1. 使得lambda捕捉的变量失去const属性,但是不说变量的修改的范围仅限于lambda表达式的内部。
  2. 在lambda的外部数据仍旧是外部的值。

->returntype:

  1. 返回值类型。
  2. 用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。
  3. 返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。
     

{statement}:

函数体,在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。

通过上述例子可以看出,lambda表达式实际上可以理解为无名函数,该函数无法直接调
用,如果想要直接调用,可借助auto将其赋值给一个变量。
 

3.lambda捕捉列表说明

捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用。

[var]:表示值传递方式捕捉变量var。

int main()
{int a = 100;int b = 200;int sum = 0;auto lam1 = [a](){cout << a << endl;};lam1();return 0;
}

[=]:表示值传递方式捕获所有父作用域中的变量(包括this)。

int main()
{int a = 100;int b = 200;int sum = 0;auto lam1 = [=](){cout << a << endl;cout << b << endl;};lam1();return 0;
}

[&var]:表示引用传递捕捉变量var。

int main()
{int a = 100;int b = 200;int sum = 0;auto lam1 = [&a](){a = 500;};lam1();cout << "a:" << a;return 0;
}

[&]:表示引用传递捕捉所有父作用域中的变量(包括this)。

int main()
{int a = 100;int b = 200;int sum = 0;auto lam1 = [&](){a = b = 50;};lam1();cout << "a:" << a << endl;cout << "b:" << b << endl;return 0;
}

 

[this]:表示值传递方式捕捉当前的this指针。

class Test
{
public:Test(int a):_a(a){}void operator()(){//捕捉this指针之后,无需使用this类指定成员,可以直接访问类成员auto Prin_a = [this]() {cout <<"class Teat::_a:" << _a << endl; };Prin_a();}private:int _a;};int main()
{Test T(100);T();return 0;
}

注意: 捕捉this指针之后,无需使用this类指定成员,可以直接访问类成员。

三.函数对象与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表达式通过捕获列表可
以直接将该变量捕获到。

UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,是一种软件建构的标准,亦为开放软件基金会组织在分布式计算环境领域的一部分。其目的,是让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。

实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如
果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。 

四.包装器

1.function包装器

function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。那么我们来看看,我们为什么需要function呢?

ret = func(x);

上面func可能是什么呢?那么func可能是函数名?函数指针?函数对象(仿函数对象)?也有可能
是lamber表达式对象?所以这些都是可调用的类型!如此丰富的类型,可能会导致模板的效率低下!为什么呢?我们继续往下看:

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表达式,是三种不同的类型,所以也就导致了模板在实例化的时候,会实例化出三份useF函数。

包装器可以很好的解决上面的问题:

// 类模板原型如下
template <class T> function;   // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参

包装上述可调用对象:

#include<functional>
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()
{function<double(double)> func1 = f;function<double(double)> func2 = Functor();function<double(double)> func3 = [](double d) {return d / 1; };useF<function<double(double)>, double>(func1, 11.11);useF<function<double(double)>, double>(func2, 11.11);useF<function<double(double)>, double>(func3, 11.11);return 0;
}

2.包装类的成员函数

对于类的成员函数,针对静态成员函数与普通函数几乎没有差异,针对类的普通成员函数,我们需要在包装的时候,传递一个类的对象,或者类的对象的指针。

class Add
{
public:Add(int a):_a(a){}int add(int b){return _a + b;}static int s_add(int a, int b){return a + b;}
private:int _a;
};int main()
{//包装非静态成员函数,用对象构建function<int(Add, int)> fun1 = &Add::add;cout << fun1(Add(10), 30) << endl;//包装非静态成员函数,用对象指针构建Add add(10);function<int(Add*, int)> fun3 = &Add::add;cout << fun3(&add, 30) << endl;//包装静态成员函数function<int(int,int)> fun2 = Add::s_add;cout << fun2(10, 30) << endl;return 0;
}

注意:包装类的非静态成员函数,使用类对象的指针包装,就不能在调用的时候用匿名对象传参,因为只有左值才能取地址。

 五.bind

std::bind函数定义在头文件<functional>中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::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函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对
象来“适应”原对象的参数列表。
调用bind的一般形式:

auto newCallable = bind(callable,arg_list);

其中,newCallable 本身是一个可调用对象,也可以使用函数对象接收,arg_list是一个逗号分隔的参数列表,对应给定的callable的参数。当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数。

arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数是“占位符”,表示
newCallable的参数,它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对
象中参数的位置:_1为newCallable的第一个参数,_2为第二个参数,以此类推。

1.调整参数位置

void Plus(int a, int b)
{cout << a << " " << b << endl;
}int main()
{//表示绑定函数plus 参数分别由调用 func1 的第一,二个参数指定function<void(int, int)> func1 = std::bind(Plus, placeholders::_1, placeholders::_2);func1(1, 2);//通过_2,_1代表的参数的位置function<void(int, int)> func2 = std::bind(Plus, placeholders::_2, placeholders::_1);func2(1, 2);return 0;
}

2.减少函数参数

通过指定一个位置的参数,在调用时少传一个参数。

 class Add
{
public:Add(int c):_c(c){}int add(int a,int b){return (a + b) * _c;}
private:int _c;
};int main()
{//包装非静态成员函数,用对象构建function<int(Add, int,int)> fun1 = &Add::add;cout << fun1(Add(10), 3,2) << endl;//包装非静态成员函数,用对象构建//指定一个参数Add(10)作为参数,剩余参数正常传参function<int(int,int)> fun2 = bind(&Add::add, Add(10), placeholders::_1, placeholders::_2);cout << fun2(3,2) << endl;return 0;
}

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

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

相关文章

成都瀚网科技:如何有效运营抖店来客呢?

随着电子商务的快速发展和移动互联网的普及&#xff0c;越来越多的企业开始将目光转向线上销售渠道。其中&#xff0c;抖音成为备受关注的平台。作为中国最大的短视频社交平台之一&#xff0c;抖音每天吸引数亿用户&#xff0c;这也为企业提供了巨大的商机。那么&#xff0c;如…

F5.5G落进现实:目标网带来的光之路

数字化与智能化的世界将走向何方&#xff1f;这个问题有着非常复杂的答案&#xff0c;但其中有一个答案已经十分清晰。那就是智能化的下一步&#xff0c;必将走向泛在万兆的世界。 网络是算力联接的底座&#xff0c;是智能演化的基础。纵观每一代数字化升级&#xff0c;都可以发…

代码随想录Day22 LeetCode T39 组合总和 T40 组合总和II T131 分割回文串

LeetCode T39 组合总和 题目链接:39. 组合总和 - 力扣&#xff08;LeetCode&#xff09; 树形图 题目思路: 这我们会发现和昨天的题目很像,只是这里的元素并不是只能选取一次了,我们可以根据代码画出树形图来解决问题,下面我们开始递归三部曲 首先我们先定义出result和path数…

亲,手撸图文博文太累了?试试这个神器!

这一篇博客有关如何使用[InternLM-XComposer]来写图文并茂的博文。InternLM-XComposer是一个基于人工智能的创作工具&#xff0c;它可以根据你的输入生成不同类型的内容&#xff0c;例如文章、诗歌、歌词、代码等。你可以使用它来创作有趣和有创意的博客&#xff0c;同时也可以…

C# OpenCvSharp 利用Lab空间把春天的场景改为秋天

效果 项目 代码 using OpenCvSharp; using System; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms;namespace OpenCvSharp_Demo {public partial class Form1 : Form{public Form1(){InitializeComponent();}st…

免费:实时 AI 编程助手 Amazon CodeWhisperer

点 &#xff0c;一起程序员弯道超车之路 现已正式推出实时 AI 编程助手 Amazon CodeWhisperer&#xff0c;包括 CodeWhisperer 个人套餐&#xff0c;所有开发人员均可免费使用。最初于去年推出的预览版 CodeWhisperer 让开发人员能够保持专注、高效&#xff0c;帮助他们快速、安…

如何管理前端状态?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

计算机网络中的CSMA/CD算法的操作流程(《自顶向下》里的提炼总结)

具有碰撞检测的载波侦听多路访问&#xff08;CSMA/CD算法&#xff09; 以下内容总结&#xff0c;对应《计算机网络自顶向下第七版》第六章链路层和局域网P299 操作流程&#xff1a; NIC&#xff08;适配器&#xff0c;即网络接口&#xff09;从网络层接收数据报&#xff0c;…

OneDrive打不开了,怎么办?使用管理员身份也无效,分享解决办法如下

文章目录 1、问题描述2、解决办法2.1 修改注册表信息2.2 修改本地组策略 1、问题描述 电脑自带的 OneDrive 突然打不开了&#xff0c;双击也没有任何反应&#xff0c;以管理员身份打开也不行。去看了好多资料才解决这个问题&#xff0c;现分享如下&#xff1b; 2、解决办法 …

用友GRP-U8 SQL注入漏洞复现

0x01 产品简介 用友GRP-U8R10行政事业财务管理软件是用友公司专注于国家电子政务事业&#xff0c;基于云计算技术所推出的新一代产品&#xff0c;是我国行政事业财务领域最专业的政府财务管理软件。 0x02 漏洞概述 用友GRP-U8的bx_historyDataCheck jsp、slbmbygr.jsp等接口存…

视频批量加水印:保护版权,提升效率

在当今的自媒体时代&#xff0c;视频制作已经成为许多人的一项必备技能。然而&#xff0c;在视频制作过程中&#xff0c;如何为自己的视频添加独特的水印以保护知识产权&#xff0c;常常让许多制作者感到困扰。本文将为你揭示如何通过固乔剪辑助手软件&#xff0c;简单几步批量…

性能测试:测试常见的指标(超详细~)

前言 今天想和大家来聊聊性能测试常见的指标&#xff0c;我在这里也不喜欢说废话我们直接开始吧。 同时&#xff0c;我也为大家准备了一份软件测试视频教程&#xff08;含面试、接口、自动化、性能测试等&#xff09;&#xff0c;就在下方&#xff0c;需要的可以直接去观看&am…

电子器件系列49:CD4050B缓冲器

同相和反向缓冲器 还搞不懂缓冲电路&#xff1f;看这一文&#xff0c;工作原理作用电路设计使用方法 - 知乎 (zhihu.com) 缓冲器_百度百科 (baidu.com) 1、缓冲器的定义 缓冲器是数字元件的其中一种&#xff0c;它对输入值不执行任何运算&#xff0c;其输出值和输入值一样&…

VR智能家居虚拟连接仿真培训系统重塑传统家居行业

家居行业基于对场景的打造及设计&#xff0c;拥有广阔前景&#xff0c;是众多行业里面成为最有可能进行元宇宙落地的应用场景之一。 家居行业十分注重场景的打造及设计&#xff0c;而元宇宙恰恰能通过将人工智能、虚拟现实、大数据、物联网等技术融合提升&#xff0c;带来身临其…

vue使用腾讯地图,实现点标记,搜索

效果图&#xff1a; 1、public文件夹下index.html添加 // public/index.html <script src"https://map.qq.com/api/js?v2.exp&key你的ksy"></script> <script src"https://map.qq.com/api/gljs?v1.exp&librariesservice&key你…

YOLOv5-训练自己的VOC格式数据集(VOC、自建数据集)

YOLOv5&#xff1a;训练自己的 VOC 格式数据集 1. 自定义数据集 1.1 环境安装 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple注意&#xff1a; 安装 lxmlPillow 版本要低于 10.0.0&#xff0c;解释链接: module ‘PIL.Image’ has no attri…

Jetpack:012-Jetpack中的弹出菜单

文章目录 1. 概念介绍2. 使用方法2.1 DropdownMenu2.2 DropdownMenuItem 3. 示例代码3.1 代码和注释3.2 代码难点3.3 运行效果 4. 内容总结 我们在上一章回中介绍了Jetpack中标题栏相关的内容&#xff0c;本章回中主要 弹出菜单。闲话休提&#xff0c;让我们一起Talk Android …

AI系统ChatGPT源码+详细搭建部署教程+支持GPT4.0+支持ai绘画(Midjourney)/支持OpenAI GPT全模型+国内AI全模型

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统AI绘画系统&#xff0c;支持OpenAI GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署…

【vSphere 8 自签名证书】企业 CA 签名证书替换 vSphere Machine SSL 证书Ⅱ—— 创建和添加证书模板

目录 博文摘要3. 使用 Microsoft 证书颁发机构创建 Machine SSL 和 Solution User 证书模板3.1 打开 Certificate Template Console3.2 复制模板3.3 修改 Compatibility 选项卡3.4 修改 General 选项卡3.5 修改 Extensions 选项卡3.6 修改 Subject Name 选项卡3.7 确认新模板 4…

软件工程与计算总结(十六)详细设计的设计模式

一.设计模式基础 某种意义上来说&#xff0c;设计模式就是设计经验的总结~ 设计模式不是简单的经验总结&#xff0c;更不是无中生有&#xff0c;它是经过实践反复检验、能解决关键技术难题、有广泛应用前景和能够显著提高软件质量的有效的经验总结。 每个模式都不是独立的&a…