【C++11】:lambda表达式function包装器
目录
- 前言
- 一,可变参数模板
- 1.1 简单认识
- 1.2 STL容器中的empalce系列相关接口
- 二,lambda表达式
- 2.1 lambda表达式语法
- 2.2 探索lambda底层
- 三,包装器
- 3.1 function包装器
- 3.2 bind
- 四,类的新功能
- 4.1 默认成员函数
- 4.2 关键字default
- 4.3 关键字delete
点击跳转上一篇文章: 【C++11】:右值引用&移动语义&完美转发
前言
上篇文章我们学习了右值引用,那是C++11中新语法的重难点。本篇文章继续学习另外两个新语法:lambda表达式&function包装器。这两个语法在以后的学习和工作中也是经常使用的,所以要重点掌握,而它们的铺垫知识可变参数模板只需略作了解即可。
一,可变参数模板
1.1 简单认识
功能:可以接受可变参数的函数模板和类模板。
下面就是一个基本可变参数的函数模板:
// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}
上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。
1.2 STL容器中的empalce系列相关接口
首先我们看到的emplace系列的接口,支持模板的可变参数,并且万能引用。那么相对 push_back 和 emplace 系列接口的优势到底在哪里呢?
其实在一般情况下,这两种接口是等价的,但是在插入pair类型时,两者就会有区别:
int main()
{bit::list<pair<bit::string, int>> lt1;// 构造pair + 拷贝/移动拷贝 pair 到 list 的节点中的data上pair<bit::string, int> kv("排序", 1);lt1.push_back(kv);// 直接构造pair参数包往下传,直接用pair参数包构造pairlt1.emplace_back("排序", 1);
}
emplace_back总体而言是更高效的。
二,lambda表达式
在C++98中,如果自定义类型排序,需要用户定义排序时的比较规则:
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& g1, const Goods& g2){return g1._price < g2._price;}
};struct ComparePriceGreater
{bool operator()(const Goods& g1, const Goods& g2){return g1._price > g2._price;}
};int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠萝", 1.5, 4 } };// 注意sort的第三个参数要用括号,因为sort是一个函数模板,参数传的是对象// 像优先级队列第三个参数就是要用类型,因为它是一个类模板sort(v.begin(), v.end(), ComparePriceLess());sort(v.begin(), v.end(), ComparePriceGreater());
}
随着C++语法的发展,人们开始觉得上面的写法太复杂了,每次为了实现一个algorithm算法, 都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,这些都给编程者带来了极大的不便。因此,在C++11语法中出现了Lambda表达式。
2.1 lambda表达式语法
(1) 语法格式:
(2) 基本使用:
int main()
{auto add1 = [](int x, int y)->int {return x + y; };cout << add1(1, 2) << endl;// 函数体中有多条语句时auto func1 = []()->int{cout << "hello bit" << endl;cout << "hello world" << endl;return 0;};func1();cout << endl
}
(3) 一些细节问题:
a. 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。
b. 参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略。
c. 返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。
d. 函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。
代码示例:
int main()
{// 无参时,参数列表可以省略,// 知道返回值类型时,返回值类型也可以省略,由编译器自动推导auto func2 = []{cout << "hello bit" << endl;cout << "hello world" << endl;return 0;};cout << func2() << endl;return 0;
}
现在来使用lambda表达式写商品排序问题:
int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };//使用lambad//价格升序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._price < g2._price;});return 0;
}
通过上述例子可以看出,lambda表达式实际上可以理解为匿名函数,该函数无法直接调用,如果想要直接调用,可借助auto将其赋值给一个变量。
2.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.015;Rate r1(rate);cout << r1(10000, 2) << endl;// lambda//捕捉列表对象的相当于以成员变量存在lambda类对象中的//捕捉的本质是构造函数的初始化参数auto r2 = [rate](double monty, int year)->double{return monty * rate * year;};cout << r2(10000, 2) << endl;int x = 1, y = 2;auto r3 = [=](double monty, int year)->double{return monty * rate * year;};cout << r3(10000, 2) << endl;return 0;
}
从使用方式上来看,函数对象与lambda表达式完全一样。
函数对象将rate作为其成员变量,在定义对象时给出初始值即可,lambda表达式通过捕获列表可以直接将该变量捕获到。
实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。
三,包装器
3.1 function包装器
function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。
使用时要包含头文件:
#include <functional>
类模板原型如下:
template <class T> function;
template <class Ret, class... Args>
class function<Ret(Args...)>;//模板参数说明:
//Ret: 被调用函数的返回类型
//Args…:被调用函数的形参
包装器是用来包装可调用对象:函数指针对象,仿函数对象,lambed。
使用方法如下:
#include <functional>int f(int a, int b)
{return a + b;
}struct Functor
{
public:int operator() (int a, int b){return a + b;}
};class Plus
{
public:static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return a + b;}
};int main()
{// 包装可调用对象:函数指针对象,仿函数对象,lambedfunction<int(int, int)> f1 = f;function<int(int, int)> f2 = Functor();function<int(int, int)> f3 = [](int x, int y){return x + y; };cout << f1(1, 1) << endl;cout << f2(1, 1) << endl;cout << f3(1, 1) << endl;// 包装静态成员函数// 受类域限制,指定类域function<int(int, int)> f4 = Plus::plusi;cout << f4(1, 1) << endl;// 包装非静态成员函数// 方式1:// 1.非静态成员函数取地址要加&符号// 2.还需要传this指针function<double(Plus*,double, double)> f5 = &Plus::plusd;Plus pd;cout << f5(&pd, 1.1, 1.1) << endl;// 方式2:// 可以不传指针,直接传对象function<double(Plus, double, double)> f6 = &Plus::plusd;cout << f6(pd, 1.1, 1.1) << endl; // 有名对象cout << f6(Plus(), 1.1, 1.1) << endl; // 匿名对象return 0;
}
3.2 bind
bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象,对可调用对象进行参数的调整(参数的顺序,参数的个数)。本质返回的是一个仿函数对象。
placeholders 是一个命名空间,在使用bind进行参数调整时,需要展开。
_1代表第一个实参
_2代表第二个实参
_3代表第三个实参
……
使用方法如下:
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;int Sub(int a, int b)
{return (a - b) * 10;
}int SubX(int a, int b, int c)
{return (a - b - c) * 10;
}int main()
{auto sub1 = bind(Sub, _1, _2);cout << sub1(10, 5) << endl;// bind 本身是一个函数模板,本质返回的是一个仿函数对象// 调整参数顺序(不常用)auto sub2 = bind(Sub, _2, _1);cout << sub2(10, 5) << endl;// 调整参数个数(常用)auto sub3 = bind(Sub, 100, _1); // 绑定第1个参数cout << sub3(5) << endl;auto sub4 = bind(Sub, _1, 100); // 绑定第2个参数cout << sub4(5) << endl;// 分别绑死第123个参数auto sub5 = bind(SubX, 100, _1, _2);cout << sub5(5, 1) << endl;auto sub6 = bind(SubX, _1, 100, _2);cout << sub6(5, 1) << endl;auto sub7 = bind(SubX, _1, _2, 100);cout << sub7(5, 1) << endl;//bind 一般用于,绑死一些固定参数function<double(Plus, double, double)> f6 = &Plus::plusd;Plus pd;cout << f6(pd, 1.1, 1.1) << endl; // 有名对象cout << f6(Plus(), 1.1, 1.1) << endl; // 匿名对象function<double(double, double)> f7 = bind(&Plus::plusd, Plus(), _1, _2);cout << f7(1.1, 1.1) << endl; // 有名对象return 0;
}
(1) 调整参数顺序示意图:
时刻记住:_1代表第一个实参,_2代表第二个实参
(2) 调整参数个数的示意图:
分别是绑定第1个参数,绑定第2个参数
四,类的新功能
4.1 默认成员函数
原来C++类中,有6个默认成员函数,C++11 新增了两个:移动构造函数和移动赋值运算符重载。
对于新增的两个默认成员函数有一些需要注意的点如下:
- 如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
- 默认移动赋值跟上面移动构造完全类似。
- 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。
代码示例如下:
class Person
{
public:Person(const char* name = "", int age = 0):_name(name), _age(age){}private:bit::string _name;int _age;
};int main()
{Person s1;Person s2 = s1;Person s3 = move(s1);Person s4;s4 = move(s2);return 0;
}
4.2 关键字default
功能:强制生成默认函数。
4.3 关键字delete
功能:禁止生成默认函数。
如果能想要限制某些默认函数的生成,在C++98中,是把该函数设置成private,并且只声明不实现,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。
class Person
{
public:Person(const char* name = "", int age = 0):_name(name), _age(age){}//防拷贝// C++98:只声明不实现,声明为私有
//private:
// Person(const Person& p);
// Person& operator=(const Person & p);//C++11//Person(const Person& p) = delete;//Person& operator=(const Person& p) = delete;private:bit::string _name;int _age;
};
相关文章:

【C++11】:lambda表达式function包装器
目录 前言一,可变参数模板1.1 简单认识1.2 STL容器中的empalce系列相关接口 二,lambda表达式2.1 lambda表达式语法2.2 探索lambda底层 三,包装器3.1 function包装器3.2 bind 四,类的新功能4.1 默认成员函数4.2 关键字default4.3 关…...

[io]进程间通信 -有名、无名管道 区别
有名管道和无名管道的区别 无名管道有名管道 使用场景 亲缘关系进程不相关的任意进程特点 1.固定读端fd[0]写端fd[1] 2.文件IO进行操作 3.不支持lseek()操作 4.数据存储在内核空间 1.文件系统中存在管道文件 2.文件IO操作 3.不支持lseek 4.先进先出 5.数…...

pywinauto:Windows桌面应用自动化测试(七)
前言 上一篇文章地址: pywinauto:Windows桌面应用自动化测试(六)-CSDN博客 下一篇文章地址: 暂无 一、实战常用方法 1、通过Desktop快速获取窗口 通过之前章节我们了解到控制应用的方法为Application࿰…...

RGB++是什么;UTXO是什么;Nervos网络;CKB区块链;
目录 RGB++是什么,简单举例说明 RGB++简介 举例说明 UTXO是什么 定义 功能与特点 使用方式 优缺点 结论 CKB区块链 一、基础属性 二、技术特点 三、经济模型 四、应用场景 Nervos网络 一、网络架构 二、技术特点 三、经济模型 四、应用场景 五、未来展望 …...

轻闪PDF v2.14.9 解锁版下载与安装教程 (全能PDF转换器)
前言 轻闪PDF(原傲软PDF编辑软件)是一款操作简单的全能PDF转换器,轻松实现PDF转换为Word,Excel或其他格式,以及PDF压缩,合并和图片文字识别OCR等功能.这款pdf编辑转换软件几乎支持所有常见文档格式,一键完成PDF与其他文档互相转换,并含有PDF合并,压缩,图片文字识别OCR等增值功…...

mysql 5.7 解析binlog日志,并统计每个类型语句(insert、update、delete)、每个表的执行次数
1、mysqlbinlog工具 使用mysqlbinlog工具将文件中执行语句解析至某个文件中。 /usr/local/mnt/mysql/bin/mysqlbinlog --base64-outputDECODE-ROWS -v /usr/local/mnt/mysql/log/mysql-bin.017278 > binlog017278.sql --base64-outputDECODE-ROWS 参数: 这个…...

MySQL案例:MHA实现主备切换(主从架构)万字详解
目录 MHA 概念 MHA的组成 特点 案例介绍 (1)案例需求 (2)案例实现思路 (3)案例拓扑图 (4)案例环境 案例步骤 基本环境配置 关闭防火墙和内核安全机制 安装数据库 授权…...

81.SAP ME - SAP SMGW Getway Monitor
目录 1.起因 2.SMGW Displaying Logged On Clients Displaying Remote Gateways Display and Control Existing Connections Deleting a Connection Displaying Gateway Release Information Displaying Parameters and Attributes of the Gateway Change Gateway Pa…...

SAPUI5基础知识24 - 如何向manifest.json中添加模型(小结)
1. 背景 在上一篇博客中,我们总结了SAPUI5中模型的各种类型,并通过代码给出了实例化这些模型的方式。 其实,在SAPUI5中,我们可以通过在manifest.json 中添加模型配置,简化模型的初始化过程,并确保模型在应…...

操作系统---文件管理
一、系统调用(系统API) 什么是系统调用 由操作系统向应用程序提供的程序接口信息,本质上就是应用程序与操作系统之间交互的接口。 操作系统的主要功能是为了管理硬件资源和为应用软件的开发人员提供一个良好的环境,使得应用程序…...

C语言指针详解(三)目录版
C语言指针详解(三)目录版 1、字符指针变量1.1、字符指针变量的一般应用1.2、常量字符串1.3、常量字符串与普通字符串的区别1.3.1 常量字符串的不可修改性1.3.2 常量字符串的存储 2、数组指针变量2.1、数组指针变量定义2.2、数组指针变量的初始化 3、二维…...

【AI资讯早报】AI科技前沿资讯概览:2024年8月6日早报
【AI资讯早报,感知未来】AI科技前沿资讯概览,涵盖了行业大会、技术创新、应用场景、行业动态等多个方面,全面展现了AI领域的最新发展动态和未来趋势。 1.【图像生成技术再突破】Midjourney V6.1震撼发布,人像生成质量跃上新台阶 …...

等保测评中的密码技术与密钥管理
在信息安全领域,等保测评(信息安全等级保护测评)是一项重要的安全评估活动,旨在评估信息系统的安全性,并根据评估结果给予相应的安全等级。这一过程中,密码技术与密钥管理发挥着至关重要的作用。本文将详细…...

go语言flag库学习
文章目录 flag基本创建使用方法正常声明全局变量指针短写 flag 基本创建使用方法 func String(name string, value string, usage string) *string func StringVar(p *string, name string, value string, usage string) 正常声明全局变量 package mainimport ("flag…...

2024年必备技能:智联招聘岗位信息采集技巧全解析
随着大数据时代的发展,精准定位职业机会成为程序员求职的关键。本文将深入解析如何利用Python高效采集智联招聘上的岗位信息,助你在2024年的职场竞争中脱颖而出。通过实战代码示例,揭示网络爬虫背后的秘密,让你轻松掌握这一必备技…...
《机器学习by周志华》学习笔记-决策树-02
1、剪枝处理(Pruning) 1.1、背景概念 上文「决策树01」的学习中,我们了解了著名的3种决策树算法ID3、C4.5、CART。这3种决策树算法最根本的核心就是根据特征选择离散属性作为节点来搭建树结构,运用搭好的结构进行推理。 剪枝(pruning)则就是将搭好的决策树去掉一些「非叶节…...

centos Python3.6升级3.8
CentOS系统上升级Python3.6到3.8版本。 步骤 1. 更新系统 在开始升级Python之前,首先需要确保系统是最新的。可以使用以下命令更新CentOS系统: sudo yum update 2. 安装依赖项 升级Python之前,需要安装一些依赖项。运行以下命令安装这些依赖…...
文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《基于竞价空间预测的虚拟电厂日前竞价策略》
本专栏栏目提供文章与程序复现思路,具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…...

Simulink模型开发中的一些自动化方法
随着Simulink模型的产品化开发进程,许多模型开发人员会关心模型的建模自动化问题。比如如何对模型中的元素进行批量查找和修改;如何构建自己的建模规则对模型进行检查;如何实现测试自动化等。在这些使用场景中我们都需要了解一些Simulink函数…...

RabbitMQ消费者消费消息失败处理
在我们开发分布式系统的过程中,RabbitMQ这样的消息队列无疑是实现微服务间通信的利器。然而,消息处理失败在所难免。当我们面临消费消息失败的情况时,该如何应对呢?在这篇博客中,我将带你深入探讨RabbitMQ消费者的消息…...

Apache Kylin分布式的分析数据仓库
Apache Kylin 是一个分布式的分析数据仓库,用于大数据上的超快在线分析处理 (OLAP)。它能够在大规模数据集上提供亚秒级的查询响应时间,并支持标准的 ANSI SQL 查询接口。Kylin 最初由 eBay 开发,后来捐赠给 Apache 软件基金会,现…...

informer中DeltaFIFO机制的实现分析与源码解读
informer中的DeltaFIFO机制的实现分析与源码解读 DeltaFIFO作为informer中重要组件,本文从源码层面了解是如何DelatFIFO是实现的。 DeltaFIFO的定义 找到delta_fifo.go的源码,位于client-go/tools/cache/delta_fifo.go 代码结构大致如下: store定义…...

树莓派下,centos7amr64下,搭建目标检测开发环境,java语言
在树莓派(Raspberry Pi)上使用CentOS 7 ARM64搭建基于Java的目标检测开发环境,可以按照以下步骤进行。需要注意的是,CentOS 7 ARM64的官方镜像可能不支持树莓派的某些硬件,因此你可能需要寻找第三方镜像或进行一些额外的配置。 1. 安装CentOS 7 ARM64 首先,确保你已经正…...

SpringBoot+Redis 发布与订阅
两个应用都引入 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artif…...

huggingface无法访问怎么办?一招教你解决,使用hf-mirror.com镜像站快速下载各种大模型
huggingface.co无法访问怎么办? 请访问 https://hf-mirror.com/ hf-mirror.com是一个旨在为中国国内的人工智能开发者提供更快更稳定下载服务的镜像站点,它镜像了Hugging Face的huggingface.co网站上的模型和数据集资源。由于网络环境和地理限制…...

如何用密码保护你的 WordPress 管理员 (wp-admin) 目录
在维护 WordPress 网站时,确保 wp-admin 目录安全是非常重要的。为该目录添加密码保护可以有效提高网站安全性,防止未经授权的访问。这篇文章将介绍实现这一目标的两种方法。 1.为什么要为 wp-admin 目录添加密码保护 WordPress 管理员后台是网站的核心…...

java 程序包org.junit.jupiter.api不存在
问题描述 正常启动springboot报错java 程序包org.junit.jupiter.api不存在。 问题分析 pom文件中缺少junit依赖,但是添加后问题仍然没解决,后面把test部分删掉解决问题。 解决方法 添加junit依赖 <dependency><groupId>junit</group…...

简单的docker学习 第4章 docker容器
第4章 Docker容器 4.1 容器基础 4.1.1 容器启动流程 通过 docker run 命令可以启动运行一个容器。该命令在执行时首先会在本地查找指定的镜像,如果找到了,则直接启动,否则会到镜像中心查找。如果镜像中心存在该镜像,则会下载到…...

零基础入门转录组数据分析——机器学习算法之SVM-RFE(筛选特征基因)
零基础入门转录组数据分析——机器学习算法之SVM-RFE(筛选特征基因) 目录 零基础入门转录组数据分析——机器学习算法之SVM-RFE(筛选特征基因)1. SVM-RFE基础知识2. SVM-RFE(Rstudio)——代码实操2. 1 数据…...

Python酷库之旅-第三方库Pandas(067)
目录 一、用法精讲 266、pandas.Series.dt.second属性 266-1、语法 266-2、参数 266-3、功能 266-4、返回值 266-5、说明 266-6、用法 266-6-1、数据准备 266-6-2、代码示例 266-6-3、结果输出 267、pandas.Series.dt.microsecond属性 267-1、语法 267-2、参数 …...