C++11新增特性:lambda表达式、function包装器、bind绑定

一、lambda表达式

1)、为啥需要引入lambda?

 在c++98中,我们使用sort对一段自定义类型进行排序的时候,每次都需要传一个仿函数,即手写一个完整的类。甚至有时需要同时实现排升序和降序,就需要各自手写一个类,非常不方便!所以C++11引入了lambda表达式!
lamaba是一个匿名函数对象,是一个可调用对象,表达式本质上也是一个类(vs中类名为lambda_uuid),并实现了operator()。

【C98玩法】:

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++11 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; });
}

2)、lambda表达式语法

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

  • capture-list(捕捉列表):编译器根据[ ]来判断接下来的代码是否为lambda函数,[ ]可以捕捉父作用域的变量供lambda函数使用。
  • parameters(列表参数):和普通函数列表参数一样。如果没用,可以连同()一起省略。
  • mutable 默认情况下lambda是一个const函数(实际上是用于修饰operator()的),mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
  • return-type:用于声明返回值类型。如果没用返回值可以省略;即使有返回值也可以省略,由编译器自动推导!
  • statement 函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。

在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情。

3)、capture-list(捕捉列表)细节

 捕捉列表用于捕捉父作用域中变量供lambda函数使用,捕捉方分为以下几种:

  1. [var]:表示值传递方式捕捉变量var。
  2. [=]:表示传值方式捕捉父作用域中的所有变量(包括this)。
  3. [&var]:表示引用传递方式捕捉变量var。
  4. [&]:表示引用方式捕捉父作用域中的所有变量(包括this)。
  5. [this]:表示值传递方式捕捉当前的this指针

 除此之外,捕捉列表可以由多个捕捉项组成,以逗号分割。比如:

     [=, &a, &b]:除引用捕捉a、b外,其他父作用域变量传值捕捉!
     [&, a, this]:值传递方式捕捉变量a和this,引用方式捕捉其他变量。

 并且捕捉列表不能重复变量传递,否则会导致编译错误。比如:[=, a]:a变量传递了两次。lambda表达式之间不能相互赋值,即使类型相同,但编译后类名都变为了lambda_uuid),类名不相同!

4)、函数对象与lambda表达式

 函数对象又称仿函数,既可以像普通函数一样使用(在类中重载operator())。而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;
}

【反汇编结果】:
在这里插入图片描述

二、function包装器

1)、包装器由来

 function包装器 也叫作适配器,包装的是可调用对象。C++中的function本质是一个类模板,也是一个包装器。
在C++中有3种可调用对象:函数指针、仿函数、lambda!但我们发现想要获得他们的都存在一些缺陷:函数指针非常麻烦,类型和对象嵌在一起;仿函数很重,即使是一个很简单的比较逻辑也要在外面定义一个类;lambda没法写类型,不同的机器和时间uuid不同,并且相对匿名(当然decltype可以获的)

除此在外,众多的可调用对象类型会导致模板的效率低下! 原因在于假设现在存在一个函数模板,我需要将一个可调用对象作为模板参数进行传递来达到某种目的。但众多的可调用对象可能实现的是同一个功能,但分别传递给模板后需要实例化3份,导致模板效率降低!!所以包装器对可调用对象进行了统一!

【实例】:

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){ return d / 4; }, 11.11) << endl;return 0;
}

2)、包装器原型

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

即现在存在一个包装器为:function<int(int, int)>,该包装器将所有的参数为2个int,返回值为int的所有可调用对象进行统一,认为是一个类型!!

3)、使用

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()
{// 函数名std::function<double(double)> func1 = f;cout << useF(func1, 11.11) << endl;// 函数对象std::function<double(double)> func2 = Functor();cout << useF(func2, 11.11) << endl;// lamber表达式std::function<double(double)> func3 = [](double d){return d;};cout << useF(func3, 11.11) << endl;return 0;
}

4)、面试题

150. 逆波兰表达式求值
在这里插入图片描述

class Solution {
public:int evalRPN(vector<string>& tokens) {unordered_map<string, function<int(int, int)>> ophash = {{"+", [](int x, int y){return x + y;}},{"-", [](int x, int y){return x - y;}},{"*", [](int x, int y){return x * y;}},{"/", [](int x, int y){return x / y;}}};stack<int> st;for(auto str : tokens){if(ophash .find(str) != ophash .end()){int right = st.top(); st.pop();int left = st.top(); st.pop();st.push(ophash [str](left, right));}elsest.push(stoi(str));}return st.top();}
};

三、bind绑定

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

1)普通绑定用法

【实例】:

int Sub(int a, int b)
{return a - b;
}int main()
{std::function<int(int, int)> func1 = std::bind(Plus, std::placeholders::_1, std::placeholders::_2);std::cout << func1(10, 2) << std::endl;
}

g.cn/direct/08ddd54d0cc649cba0b6d963bd07d864.png)

 bind函数中,_1和_2都是占位符。当执行func1(10, 2)函数时,参数10和2会被作为参数传递给Plus函数。其中std::placeholders::_1表示func1第一个参数的位置, std::placeholders::_2表示第二个参数所在位置!

2)、改变参数位置

 既然_1和_2是占位符,所以仅需改变两者位置,所以func函数传递给Plus的参数即可转换。具体结果如下:

int Sub(int a, int b)
{return a - b;
}int main()
{std::function<int(int, int)> func1 = std::bind(Sub, std::placeholders::_2, std::placeholders::_1);std::cout << func1(10, 2) << std::endl;
}

在这里插入图片描述

3)改变参数的个数

 我们在类外调用一个类成员函数,调用成员函数时第一个参数为该对象的指针。但每次我们手动传递的时候非常麻烦。所以我们可以通过bind()将第一个参数进行绑死,改变参数个数。具体如下:

class Plus
{
public:static int Plusi(int x, int y){return x + y;}double Plusd(double x, double y){return x + y;}
};
int main()
{Plus ps;// 正常情况下,第一个参数this指针需要手动传递, 麻烦std::function<double(Plus*, double, double)> func1 = &Plus::Plusd;std::cout << func1(&ps, 10.1, 20.2) << std::endl;// 将第一个参数绑死为Plus*, std::function<double(double, double)> func2 = std::bind(&Plus::Plusd,&ps, std::placeholders::_1, std::placeholders::_2);std::cout << func2(10.1, 20.2) << std::endl;// 将第一个参数和第三个参数绑死std::function<double(double)> func3 = std::bind(&Plus::Plusd, &ps, std::placeholders::_1, 1.1);std::cout << func3(10.1) << std::endl;// 所有参数直接全部绑死, 注意此处博主绑定的是静态函数auto func4 = std::bind(&Plus::Plusi, 10, 10);std::cout << func4() << std::endl;//绑定时,第一个参数因为指针,但编译器做了特殊处理,可以绑定临时对象std::function<double(double, double)> func5 = std::bind(&Plus::Plusd, Plus(), std::placeholders::_1, std::placeholders::_2);std::cout << func5(1.1, 2.2) << std::endl;
}

在这里插入图片描述

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

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

相关文章

信息学奥赛初赛天天练-91-CSP-S2023基础题3-编译命令、树的重心、拓扑排序、进制转换、R进制转十进制、十进制转R进制

PDF文档公众号回复关键字:20240917 2023 CSP-S 选择题 1单项选择题&#xff08;共15题&#xff0c;每题2分&#xff0c;共计30分&#xff1a;每题有且仅有一个正确选项&#xff09; 11 以下哪个命令&#xff0c;能将一个名为 main.cpp 的 C 源文件&#xff0c;编译并生成一个…

[Unity Demo]从零开始制作空洞骑士Hollow Knight第二集:通过InControl插件实现绑定玩家输入以及制作小骑士移动空闲动画

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、通过InControl插件实现绑定玩家输入二、制作小骑士移动和空闲动画 1.制作动画2.玩家移动和翻转图像3.状态机思想实现动画切换总结 前言 好久没来CSDN看看&…

利用JS数组根据数据生成柱形图

要求 <html> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document…

Python 基础 (标准库):datetime (基本日期和时间类型)

1. 官方文档 datetime --- 基本日期和时间类型 — Python 3.12.2 文档 tz — dateutil 3.9.0 documentation 2. 背景 2.1 处理时间数据的难点 计算机程序喜欢有序的、有规则的事件&#xff0c;但对于时间数据&#xff0c;其涉及的规则复杂且可能有变化&#xff0c;最典型例…

【homebrew安装】踩坑爬坑教程

homebrew官网&#xff0c;有安装教程提示&#xff0c;但是在实际安装时&#xff0c;由于待下载的包的尺寸过大&#xff0c;本地git缓存尺寸、超时时间的限制&#xff0c;会报如下错误&#xff1a; error: RPC failed; curl 92 HTTP/2 stream 5 was not closed cleanly&#xf…

2024永久激活版 Studio One 6 Pro for mac 音乐创作编辑软件 完美兼容

Studio One 6是一款功能强大的音乐制作软件&#xff0c;由PreSonus公司开发。它提供了全面的音频录制、编辑、混音和母带处理工具&#xff0c;适用于音乐制作人、音频工程师和创作人员。 Studio One 6拥有直观的用户界面&#xff0c;使用户能够快速而流畅地进行音乐创作。它采…

华为HarmonyOS地图服务 -- 如何实现地图呈现?-- HarmonyOS自学8

如何使用地图组件MapComponent和MapComponentController呈现地图&#xff0c;效果如下图所示。 MapComponent是地图组件&#xff0c;用于在您的页面中放置地图。MapComponentController是地图组件的主要功能入口类&#xff0c;用来操作地图&#xff0c;与地图有关的所有方法从此…

基于 onsemi NCV78343 NCV78964的汽车矩阵式大灯方案

一、方案描述 大联大世平集团针对汽车矩阵大灯&#xff0c;推出 基于 onsemi NCV78343 & NCV78964的汽车矩阵式大灯方案。 开发板搭载的主要器件有 onsemi 的 Matrix Controller NCV78343、LED Driver NCV78964、Motor Driver NCV70517、以及 NXP 的 MCU S32K344。 二、开…

【我的 PWN 学习手札】Fastbin Double Free

前言 Fastbin的Double Free实际上还是利用其特性产生UAF的效果&#xff0c;使得可以进行Fastbin Attack 一、Double Free double free&#xff0c;顾名思义&#xff0c;free两次。对于fastbin这种单链表的组织结构&#xff0c;会形成这样一个效果&#xff1a; 如果我们mallo…

记一次实战中对fastjson waf的绕过

最近遇到一个fastjson的站&#xff0c;很明显是有fastjson漏洞的&#xff0c;因为type这种字符&#xff0c;fastjson特征很明显的字符都被过滤了 于是开始了绕过之旅&#xff0c;顺便来学习一下如何waf 编码绕过 去网上搜索还是有绕过waf的文章&#xff0c;下面来分析一手&a…

分布式训练:(Pytorch)

分布式训练是将机器学习模型的训练过程分散到多个计算节点或设备上&#xff0c;以提高训练速度和效率&#xff0c;尤其是在处理大规模数据和模型时。分布式训练主要分为数据并行和模型并行两种主要策略&#xff1a; 1. 数据并行 (Data Parallelism) 数据并行是最常见的分布式…

【网络安全】逻辑漏洞之购买商品

未经授权,不得转载。 文章目录 正文正文 电子商务平台的核心功能,即购买商品功能。因为在这个场景下,任何功能错误都有可能对平台产生重大影响,特别是与商品价格和数量有关的问题。 将商品添加到购物车时拦截请求: 请求包的参数: 解码参数后,并没有发现价格相关的参数,…

Python(TensorFlow和PyTorch)及C++注意力网络导图

&#x1f3af;要点 谱图神经网络计算注意力分数对比图神经网络、卷积网络和图注意力网络药物靶标建模学习和预测相互作用腹侧和背侧皮质下结构手写字体字符序列文本识别组织病理学图像分析长短期记忆财务模式预测相关性生物医学图像特征学习和迭代纠正 Python注意力机制 对…

AE VM5000 Platform VarioMatch Match Network 手侧

AE VM5000 Platform VarioMatch Match Network 手侧

算法入门-贪心1

第八部分&#xff1a;贪心 409.最长回文串&#xff08;简单&#xff09; 给定一个包含大写字母和小写字母的字符串 s &#xff0c;返回通过这些字母构造成的最长的回文串 的长度。 在构造过程中&#xff0c;请注意 区分大小写 。比如 "Aa" 不能当做一个回文字符串…

Understanding the model of openAI 5 (1024 unit LSTM reinforcement learning)

题意&#xff1a;理解 OpenAI 5&#xff08;1024 单元 LSTM 强化学习&#xff09;的模型 问题背景&#xff1a; I recently came across openAI 5. I was curious to see how their model is built and understand it. I read in wikipedia that it "contains a single l…

从0-1 用AI做一个赚钱的小红书账号(不是广告不是广告)

大家好&#xff0c;我是胡广&#xff01;是不是被标题吸引过来的呢&#xff1f;是不是觉得自己天赋异禀&#xff0c;肯定是那万中无一的赚钱天才。哈哈哈&#xff0c;我告诉你&#xff0c;你我皆是牛马&#xff0c;不要老想着突然就成功了&#xff0c;一夜暴富了&#xff0c;瞬…

【SQL】百题计划:SQL对于空值的比较判断。

[SQL]百题计划 方法&#xff1a; 使用 <> (!) 和 IS NULL [Accepted] 想法 有的人也许会非常直观地想到如下解法。 SELECT name FROM customer WHERE referee_Id <> 2;然而&#xff0c;这个查询只会返回一个结果&#xff1a;Zach&#xff0c;尽管事实上有 4 个…

React js Router 路由 2, (把写过的几个 app 组合起来)

完整的项目&#xff0c;我已经上传了&#xff0c;资源链接. 起因&#xff0c; 目的: 每次都是新建一个 react 项目&#xff0c;有点繁琐。 刚刚学了路由&#xff0c;不如写一个 大一点的 app &#xff0c;把前面写过的几个 app, 都包含进去。 这部分感觉就像是&#xff0c; …

linux网络编程——UDP编程

写在前边 本文是B站up主韦东山的4_8-3.UDP编程示例_哔哩哔哩_bilibili视频的笔记&#xff0c;其中有些部分博主也没有理解&#xff0c;希望各位辩证的看。 UDP协议简介 UDP 是一个简单的面向数据报的运输层协议&#xff0c;在网络中用于处理数据包&#xff0c;是一种无连接的…