【c++11】c++11新特性(下)(可变参数模板、default和delete、容器新设定、包装器)
🌟🌟作者主页:ephemerals__
🌟🌟所属专栏:C++
目录
前言
五、可变参数模板
1. 概念及简单定义
2. 包扩展
六、 default和delete
七、容器新设定
1. 新容器
2. 新接口
emplace系列接口
八、函数包装器
1. function
2. bind
总结
前言
之前我们学习了c++11的部分新特性:列表初始化、右值引用、类的新默认成员函数和lambda表达式等:
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)-CSDN博客
本篇文章,我们继续学习其余几个:可变参数模板、default和delete、容器新设定、包装器。
正文开始
五、可变参数模板
1. 概念及简单定义
所谓可变参数模板,就是指模板可以接收可变数量的参数。通过处理不同数量的参数类型,代码的灵活性和重用性大大提高。
可变参数模板可以用于类模板,也可用于函数模板。
可变参数模板不仅支持类型实例化,也可支持个数实例化。(参数数量可以变化)
当然,可变参数的函数模板当中,其函数的参数也是可变的。
接下来我们定义一个简易的可变参数模板:
#include <iostream>
using namespace std;//可变参数模板
template<class ...Args>
void print(Args... args)
{cout << sizeof...(args) << endl;
}int main()
{print(1);print(1, 2, 3);print('a', "xxxx", 3.5, 9);return 0;
}
这里有几点需要注意:
1. 模板参数定义时,要在前面加上“...”,表示这是一个可变模板参数。这里的Args叫做“模板参数包”,它可以接收多个任意类型的参数,在编译时按需实例化。
2. 函数参数的类型使用模板参数包进行定义,其变量名args叫做“函数参数包”,表示多个函数形参。注意函数参数包前面要加上“...”。
3. 模板参数包和函数参数包可以任意取名,不一定非得是“Args”或者“args”。
4. 函数体当中,我们使用了一个新的运算符“sizeof...”,它用于计算函数参数包中的参数个数。
程序运行结果:
这里我们调用了三次print函数,这三次调用使得可变参数模板的形参列表被分别实例化为:
void print<int>(int args);
void print<int, int, int>(int args, int args, int args);
void print<char, const char *, double, int>(char args, const char *args, double args, int args);
因此,可变参数模板和普通模板的原理还是类似的,本质还是按需实例化,只不过额外支持了参数个数的实例化。
当然,可变参数模板的函数形参也可以用左值引用或右值引用表示,其折叠规则与普通模板相同。
void print(Args... args)
void print(Args&... args)
void print(Args&&... args)
2. 包扩展
刚才我们定义的可变参数模板当中,只是计算了参数的个数并输出,若只能如此的话,未免也太缺乏实用性了。那么能否对每一个参数进行相应的处理呢? 这里就要使用“包扩展”了。
所谓“包扩展”,是c++11引入的一个机制,用于分解出函数参数包中的一个个参数。要实现包扩展,就要使用包扩展运算符“...”。
一个简单的使用示例:
#include <iostream>
using namespace std;//可变参数模板
template<class ...Args>
void print(Args... args)
{(cout << ... << args) << endl;
}int main()
{print(1, 2, 'a');return 0;
}
运行结果:
这里我们使用了“...”将args进行扩展,然后一个个地输出。但这种方法的原理较难理解,并且灵活性也较差,所以我们通常用编译时递归实例化的方式进行包扩展。示例如下:
#include <iostream>
using namespace std;void ShowList()//递归出口,无参
{cout << endl;
}template<class T, class ...Args>
void ShowList(T&& x, Args&&... args)//接收第一个参数和剩余参数包
{cout << x << ' ';//打印第一个参数ShowList(forward<Args>(args)...);//将剩余参数包一个个展开,并传入,递归调用
}template<class ...Args>
void Print(Args&&... args)
{ShowList(forward<Args>(args)...);//将参数包一个个展开,并传入
}int main()
{Print(1, 2, 'a');//函数调用return 0;
}
运行结果:
不难发现,这里的扩展方法十分巧妙:先使用Print将多个参数打包成函数参数包,然后使用“...”进行扩展,并传入ShowList。ShowList也是一个可变参数模板,它将接收到的参数分解成两部分:第一个参数和剩余部分,并将剩余部分打包成一个新的参数包。函数体内部打印了第一个参数后,将剩余参数包再传给自己去调用,这样递归地传入下去,编译器在编译时一个个地按需实例化对应版本,就能够一个一个地输出所有参数了。而当所有参数都被打印后,再次传给自己的参数包就是无参的,此时由于我们定义了一个同名无参函数,当函数模板和同名函数同时符合参数要求时,会优先调用同名函数,这样就相当于递归出口,终止了函数模板的递归调用。
注:这里将参数进行完美转发后传入,是为了保持右值属性,减少递归时的拷贝。
需要注意:传入参数包时,一定要配合包扩展运算符,将参数包分解之后再传入;注意将包括展运算符与参数列表中的“...”区分开,它们不是同一个含义;包扩展运算符要写在参数包之后。
当然,递归式的包扩展也可以这样实现,原理是一样的:
template <class T>
void Print(T&& x)
{cout << x << ' ';
}template <class T, class ... Args>
void Print(T&& x, Args&&... args)
{cout << x << ' ';Print(forward<Args>(args)...);
}
六、 default和delete
c++11引入了default和delete这两个关键字(用于类的定义中),它们的作用是对类的默认成员函数进行显式的控制。
假如你要使用某个默认生成的成员函数,但由于一些原因,并没有默认生成,此时你就可以使用default强制生成对应默认成员函数。使用示例:
class A
{
public:A(const A& a)//只显式实现了拷贝构造,因此不会生成默认的移动构造和构造函数{//...}A(A&& a) = default;//强制生成移动构造A() = default;//强制生成默认构造函数
private://...
};
如果你想限制某些默认成员函数,使其不可被外界显式调用,可以将其设置为私有成员,也可以使用delete关键字,将其强制删除。示例:
class A
{
public:A& operator=(const A& a) = delete;//强制删除赋值重载private://...
};
注:强制删除某个默认成员函数后,该成员函数也不可显式实现。
七、容器新设定
1. 新容器
c++11引入了几个新的STL容器,例如array(定长数组)、forward_list(单链表)、unordered系列容器。其中最为实用的还是unordered系列容器,可以将哈希的高效性充分融入到实际开发中。
c++11 STL容器介绍与使用方法查阅:Reference - C++ Reference
2. 新接口
由于右值引用和initializer_list的出现,c++11还引入了容器插入、拷贝、初始化相关的全新重载版本,提高了效率。
emplace系列接口
c++11还引入了容器的emplace系列接口,相比push_back、insert等接口,它的特点是直接在容器内部构造元素,而不是而不是先创建临时对象再拷贝或移动到容器中,更加高效。
举个例子:
#include <iostream>
#include <list>
#include <string>
using namespace std;int main()
{list<string> l;l.push_back("hello world");l.emplace_back("hello world");return 0;
}
这里我们分别调用push_back、emplace_back给list尾插两个字符串,相比push_back,emplace_back会更加高效。因为调用push_back时,首先会将"hello world"构造为string类型得临时对象,然后再执行插入操作,最后析构这个string的临时对象;而emplace_back是一个可变参数模板,传入"hello world"之后,被实例化为const str*类型,之后直接用字符串构造出一个节点,避免产生string的临时对象,效率更高。
并且,由于其是可变参数模板,所以也支持多个参数传入,并构造节点:
list<pair<string, int>> l;
l.push_back({ "苹果",1 });
l.emplace_back("苹果", 1); // 多参数传入
当然,许多容器除了emplace_back,还有emplace_front、emplace接口,分别支持头插和任意位置的插入操作。
八、函数包装器
函数包装器是c++11引入的工具,它可以对函数进行“封装”,后续需要时调用,功能类似于函数指针,且更加安全。c++11的函数包装器一共有两个:function和bind。
1. function
function本质是一个类模板,包含在头文件<functional>中,它可以接收一个可调用对象,例如函数指针、lambda表达式、仿函数对象等,并可以通过function调用它。这样就可以将不同类型的可调用对象统一化。示例:
#include <iostream>
#include <functional>
using namespace std;//函数
int f1(int a, int b)
{return a + b;
}//仿函数
struct F2
{int operator()(int a, int b){return a - b;}
}f2; //lambda表达式
auto f3 = [](int a, int b)->int{ return a * b; };int main()
{//定义包装器,其接收的可调用对象,参数为两个int,返回值为intfunction<int(int, int)> f;//使用包装器调用可调用对象f = f1; // 接收函数cout << f(3, 5) << endl;f = f2; // 接收仿函数对象cout << f(3, 5) << endl;f = f3; // 接收lambda表达式cout << f(3, 5) << endl;return 0;
}
运行结果:
定义function时,注意要显式实例化要接收对象的参数和返回值;调用时,直接用函数调用的方式进行传参。
注意:用function包装非静态成员函数时,别忘记第一个参数是this指针,显式实例化时要将对象指针类型写在参数列表中:
#include <iostream>
#include <functional>
using namespace std;class A
{
public:A(int n, int m):_n(n), _m(m){}void Print(){cout << _n + _m << endl;}
private:int _n;int _m;
};int main()
{A a(1, 2);function<void(A*)> f1 = &A::Print; // 包装成员函数Printf1(&a); // 调用时传入对象地址return 0;
}
当然,参数也可以不设置为对象指针,可以直接设置成对象本身或对象的引用类型,传参时传入对象即可:
function<void(A)> f2 = &A::Print;
f2(a);function<void(A&)> f3 = &A::Print;
f3(a);function<void(A&&)> f4 = &A::Print;
f4(move(a));
2. bind
bind的本质是一个函数模板, 包含在头文件<functional>中,它也可以接收可调用对象,但与function不同的是,它可以返回一个在原对象基础上调整传参顺序或增加默认参数的可调用对象,更加灵活。
它的基本使用方式如下:
#include <iostream>
#include <functional>
using namespace std;
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;void fun(int a, int b, int c)
{cout << a << endl;cout << b << endl;cout << c << endl;
}int main()
{auto f = bind(fun, _1, _2, _3); // 包装fun函数//函数调用f(1, 2, 3);return 0;
}
运行结果:
使用bind包装fun函数时,bind的参数列表第一个位置是fun函数的地址;其余位置表示要传入的参数的代号(注意这个代号在std的placeholders空间中,这里为了方便直接展开了)。然后用一个auto变量接收bind的返回值,就可以对fun函数进行调用。
改变bind的代号的顺序,就可以调整传参顺序:
int main()
{auto f = bind(fun, _1, _3, _2); // 调整第二个参数和第三个参数的相对位置//函数调用f(10, 20, 30);return 0;
}
运行结果:
这里我们在包装fun时,交换了第三个参数和第二个参数的相对位置,调用fun时的参数就会按照包装时的顺序进行传入,而不是从左到右的顺序,这样可以使一些原型不一致的函数进行适配。
在bind代号位置传入初值,就可以为任意参数设置初值:
int main()
{auto f = bind(fun, _1, 5, _2); // 给第二个参数设置初值5,注意这里的_2不是表示函数的第二个参数,而是调用时传入的第二个参数//函数调用f(10, 20);return 0;
}
运行结果:
相比缺省参数,它的好处是可以给任意位置的参数设置初值,而缺省参数只能从右到左设初值。
有了bind之后,我们就可以绑死一些参数,例如成员函数的对象,这样每次调用成员函数就不用显式写明对象了。
总结
本篇文章我们学习了c++11的四个新语法:可变参数模板、default和delete、容器的新设定和函数包装器。它们大大提高了c++编程的灵活性,也提高了我们的开发效率。之后博主会和大家分享智能指针以及其他包装器的相关知识和用法。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤
相关文章:

【c++11】c++11新特性(下)(可变参数模板、default和delete、容器新设定、包装器)
🌟🌟作者主页:ephemerals__ 🌟🌟所属专栏:C 目录 前言 五、可变参数模板 1. 概念及简单定义 2. 包扩展 六、 default和delete 七、容器新设定 1. 新容器 2. 新接口 emplace系列接口 八、函数包…...

PyTorch 实现食物图像分类实战:从数据处理到模型训练
一、简介 在计算机视觉领域,图像分类是一项基础且重要的任务,广泛应用于智能安防、医疗诊断、电商推荐等场景。本文将以食物图像分类为例,基于 PyTorch 框架,详细介绍从数据准备、模型构建到训练测试的全流程,帮助读者…...

Qt —— 在Linux下试用QWebEngingView出现的Js错误问题解决(附上四种解决办法)
错误提示:js: A parser-blocking, cross site (i.e. different eTLD+1) script, https:xxxx, is invoked via document.write. The network request for this script MAY be blocked by the browser in this or a future page load due to poor network connectivity. If bloc…...

命名空间(C++)
命名空间主要用于大型项目中。 局部命名在该局部会覆盖全局命名。C语言中唯一一种在局部调用全局相同命名的全局变量的方式:指针在C中可以用作用域运算符来访问全局变量,作用域运算符的前面可以是作用域也可以是类。 命名空间实际上是对全局作用域的再次…...
使用Python脚本在Mac上彻底清除Chrome浏览历史:开发实战与隐私保护指南
题目: 《基于PyCharm与Mac系统的Chrome历史记录清理工具开发实战》 引言 在Mac系统下,Chrome浏览器的历史记录文件通常以SQLite数据库形式存储于用户目录中,仅通过浏览器内置功能清理可能残留索引文件。本文通过一个Python脚本(c…...

LabVIEW圆锥滚子视觉检测系统
基于LabVIEW平台的视觉检测系统提高圆锥滚子内组件的生产质量和效率。通过集成高分辨率摄像头和先进的图像处理算法,系统能够自动识别和分类产品缺陷,从而减少人工检查需求,提高检测的准确性和速度。 项目背景 随着制造业对产品质…...

OpenAI 推出「轻量级」Deep Research,免费用户同享
刚刚,OpenAI 正式上线了面向所有用户的「轻量级」Deep Research 版本,意味着即便没有付费订阅,也能体验这一强大工具的核心功能。 核心差异:o4-mini vs. o3 模型迭代 传统的深度研究功能基于更大规模的 o3 模型。轻量级版本则改以…...

罗伯·派克:Go语言创始者的极客人生
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 罗伯派克:Go语言创始者的极客人生 一、传奇程序员的成长历程 1. 早年经历…...
面试题:Redis 一次性获取大量Key的风险及优化方案
Redis 一次性获取大量Key的风险及优化方案 在Redis中一次性获取大量Key(如使用KEYS命令或大量GET操作)会带来多种风险和性能问题,以下是详细分析和解决方案: 主要风险 1. 阻塞风险 KEYS命令阻塞:KEYS *会扫描整个数…...
中国头部云服务商分析
1. 阿里云 国内云服务的开创者与龙头,占据约三分之一的国内市场份额,其中IaaS占比72%,PaaS与SaaS占比相对较小 全球范围内500万客户,基础设施目前面向全球四大洲,开服运营15个国家、30个公共云地域、89个可用区&#x…...
关于使用git init --bare 裸仓库的使用
1、创建文件夹 对于需要作为仓库的文件夹使用git init --bare进行裸仓库初始化 2、将裸仓库添加为自己的远程仓库 使用的方法和添加远程仓库的方式相同,但是路径需要为绝对路径,同时需要加入file:///协议 git remote add origin file:///d:/Desktop/Lo…...
解释一下计算机中的内存对齐
1. 内存对齐的基本概念 内存对齐是计算机系统优化内存访问效率的一种机制,要求数据在内存中的起始地址必须为某个值的整数倍(通常为数据类型大小的整数倍)。例如: int (4字节) 应对齐到4的倍数地址(如0x00, 0x04, 0x…...

小白工具视频转MPG, 功能丰富齐全,无需下载软件,在线使用,超实用
在视频格式转换需求日益多样的今天,小白工具网的在线视频转 MPG 功能https://www.xiaobaitool.net/videos/convert-to-mpg/ )脱颖而出,凭借其出色特性,成为众多用户处理视频格式转换的优质选择。 从格式兼容性来看,它支…...
跟着deepseek学golang--认识golang
文章目录 一、Golang核心优势1. 极简部署方式生产案例:依赖管理:容器实践: 2. 静态类型系统类型安全示例:性能优势:代码重构: 3. 语言级并发支持GMP调度模型实例&…...
目前市面上知名的数据采集器
程序员爱自己动手打造一切,但这样离钱就会比较远。 市面上知名的数据采集工具 数据采集工具(也称为网络爬虫或数据抓取工具)在市场上有很多选择,以下是目前比较知名和广泛使用的工具分类介绍: 一、开源免费工具 Scra…...
问答页面支持拖拽和复制粘贴文件,MaxKB企业级AI助手v1.10.6 LTS版本发布
2025年4月24日,MaxKB开源企业级AI助手正式发布v1.10.6 LTS版本。这一版本主要进行了一些功能优化和问题修复。 功能优化 ■ 应用:文件上传支持上传其他自定义的文件类型,该类型文件需要自行写入函数解析; ■ 问答页面ÿ…...

day32 学习笔记
文章目录 前言一、霍夫变换二、标准霍夫变换三、统计概率霍夫变换四、霍夫圆变换 前言 通过今天的学习,我掌握了霍夫变换的基本原本原理及其在OpenCV中的应用方法 一、霍夫变换 霍夫变换是图像处理中的常用技术,主要用于检测图像中的直线,圆…...
二项分布详解:从基础到应用
二项分布详解:从基础到应用 目录 引言二项分布的定义概率质量函数及其证明期望与方差推导二项分布的重要性质常见应用场景与其他分布的关系知识梳理练习与思考 引言 概率论中,二项分布是最基础也是最常用的离散概率分布之一。它描述了在固定次数的独…...

CentOS 7上Memcached的安装、配置及高可用架构搭建
Memcached是一款高性能的分布式内存缓存系统,常用于加速动态Web应用的响应。本文将在CentOS 7上详细介绍Memcached的安装、配置,以及如何实现Memcached的高可用架构。 (1)、搭建memcached 主主复制架构 Memcached 的复制功能支持…...

如何让 HTML 文件嵌入另一个 HTML 文件:详解与实践
目录 一、为什么需要在HTML中嵌入其他HTML文件? 二、常用的方法概览 三、利用 1. 基本原理 2. 使用场景 3. 优缺点 4. 实践示例 5. 适用建议 四、利用JavaScript动态加载内容 1. 原理简介 2. 实现步骤 示例代码 3. 优缺点分析 4. 应用场景 5. 实践建…...
mac brew 无法找到php7.2 如何安装php7.2
mac brew 无法找到php7.2 如何安装php7.2 原因是升级过高版本的brew后已经不支持7.2了,但可以通过第三方工具来安装 brew tap shivammathur/php brew install shivammathur/php/php7.2标题安装完成后会提示以下信息: The php.ini and php-fpm.ini fil…...

人工智能与机器学习:Python从零实现逻辑回归模型
🧠 向所有学习者致敬! “学习不是装满一桶水,而是点燃一把火。” —— 叶芝 我的博客主页: https://lizheng.blog.csdn.net 🌐 欢迎点击加入AI人工智能社区! 🚀 让我们一起努力,共创…...

windows服务器及网络:搭建FTP服务器
前言:(各位大佬们,昨天太忙了,整得没有发布昨天那该写的那一篇,属实有点可惜的说QAQ,不过问题已经解决,我又回来啦) 今天我要介绍的是在Windows中关于搭建FTP服务器的流程与方法 注…...
Python学习之路(五)-接口API
在 Python 中结合数据库开发接口 API 通常使用 Web 框架(如 Flask 或 Django)和 ORM(对象关系映射)工具(如 SQLAlchemy 或 Django ORM)。以下是使用 Flask 和 SQLAlchemy 的详细步骤,展示如何结合数据库开发一个简单的 API。 使用 Flask 和 SQLAlchemy 开发 API 1. 安…...

欧拉计划 Project Euler56(幂的数字和)题解
欧拉计划 Project Euler 56 题解 题干思路code 题干 思路 直接暴力枚举即可,用c要模拟大数的乘法,否则会溢出 code // 972 #include <bits/stdc.h>using namespace std;using ll long long;string mul(const string &num1, int num2) {int…...

C++初窥门径
const关键字 一、const关键字 修饰成员变量 常成员变量:必须通过构造函数的初始化列表进行初始化,且初始化后不可修改。 示例: class Student { private: const int age; // 常成员变量 public: Student(string name, int age) : age(ag…...

AlarmClock4.8.4(官方版)桌面时钟工具软件下载安装教程
1.软件名称:AlarmClock 2.软件版本:4.8.4 3.软件大小:187 MB 4.安装环境:win7/win10/win11(64位) 5.下载地址: https://www.kdocs.cn/l/cdZMwizD2ZL1?RL1MvMTM%3D 提示:先转存后下载,防止资…...

白鲸开源WhaleStudio与崖山数据库管理系统YashanDB完成产品兼容互认证
近日,北京白鲸开源科技有限公司与深圳计算科学研究院联合宣布,双方已完成产品兼容互认证。此次认证涉及深圳计算科学研究院自主研发的崖山数据库管理系统YashanDB V23和北京白鲸开源科技有限公司的核心产品WhaleStudio V2.6。经过严格的测试与验证&#…...

【金仓数据库征文】- 金融HTAP实战:KingbaseES实时风控与毫秒级分析一体化架构
文章目录 引言:金融数字化转型的HTAP引擎革命一、HTAP架构设计与资源隔离策略1.1 混合负载物理隔离架构1.1.1 行列存储分区策略1.1.2 四级资源隔离机制 二、实时流处理与增量同步优化2.1 分钟级新鲜度保障2.1.1 WAL日志增量同步2.1.2 流计算优化 2.2 物化视图实时刷…...
云服务器centos 安装hadoop集群
百度 搜索 云服务器centos 安装hadoop 创建Hadoop用户 sudo useradd hadoop -m -s /bin/bash sudo passwd hadoop 123456 下载Hadoop wget https://mirrors.tuna.tsinghua.edu.cn/apache/hadoop/common/hadoop-3.2.4/hadoop-3.2.4.tar.gz 解压并移动Hadoop到指定目录 tar …...