当前位置: 首页 > news >正文

成都那家网站做的好网站空间文件删不掉

成都那家网站做的好,网站空间文件删不掉,lamp安装wordpress,php网站开发试题及答案目录 可变模板的定义方式 参数包的展开方式 递归的方式展开参数包 STL中的emplace相关接口函数 STL容器中emplace相关插入接口函数 ​编辑 模拟实现#xff1a;emplace接口 C11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板#xff0c;相比 C9…目录 可变模板的定义方式 参数包的展开方式 递归的方式展开参数包 STL中的emplace相关接口函数 STL容器中emplace相关插入接口函数 ​编辑 模拟实现emplace接口 C11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板相比 C98/03类模版和函数模版中只能含固定数量的模版参数可变模版参数无疑是一个巨大的改 进。 像之前学习的printf就是一个函数参数的可变参数它可以接收多个任意类型但它们只函数参数的可变参数并不是模板的可变参数 printf的使用方法 int printf( const char *format , ... ); 本博客讲解的是函数模板的可变参数不会涉及到类模板的可变参数 可变模板的定义方式 函数的可变参数模板定义方式如下 templateclass ...Args //Args全称arguments 返回类型 函数名Args... args {//函数体 } 下面就是一个基本可变参数的函数模板 template class ...Args void ShowList(Args... args) {} Args是一个可变模板参数包 args是一个函数形参参数包 说明一下 模板参数Args前面有省略号代表它是一个可变模板参数我们将带省略号的参数称为 “参数包”这个参数包中可以包含0到任意个模板参数args则是一个函数形参参数包 现在我们可以向这个函数中传入多个不同的类型并且可以通过sizeof算出参数包的参数个数 以下例代码为例 templateclass ...Args void ShowList(Args... args) {cout sizeof...(args) endl; }int main() {ShowList(1);ShowList(1, 2);ShowList(1, 2, string(dict));mapstring, int m1;ShowList(1, 2, 3, m1);return 0; } 我们无法直接获取参数包args中的每个参数的 只能通过展开参数包的方式来获取参数包中的每个参数这是使用可变模版参数的一个主要特点也是最大的难点即如何展开可变模版参数。 由于C11语法不支持使用args[i]这样方式获取可变q参数所以我们的用一些奇招来一一获取参数包的值。 错误示例 templateclass ...Args void ShowList(Args... args) {//errorfor (int i 0; i sizeof...(args); i){cout args[i] endl;} } 参数包的展开方式 递归的方式展开参数包 方式如下 1.给函数模板新增一个参数这样就可以从接收到的参数包分离出来一个参数 2.在函数模板中进行递归不断的分离参数包中的参数 3.直到接收到最后一个参数结束 结束条件 -1. 可以创建一个无参的函数来终止递归当参数包中的参数为0时会调用该函数终止循环 void _ShowList() {cout endl; }templateclass T, class ...Args void _ShowList(T value, Args... args) {cout value ;_ShowList(args...); }int main() {_ShowList(1, 2, string(dict));return 0; } -2. 可以创建一个参数的函数来终止递归当参数包中的参数为1时会调用该函数终止循环 templateclass T void _ShowList(const T t) {cout t endl; }templateclass T, class ...Args void _ShowList(T value, Args... args) {cout value ;_ShowList(args...); }int main() {_ShowList(1, 2, string(dict));return 0; } 但是使用该方法有一个弊端我们在调用ShowList函数时必须至少传入一个参数否则就会报错因为此时无论是调用递归终止函数还是展开函数都需要至少传入一个参数 使用sizeof...(args)算出参数个数的特性利用它的特性做一个递归结束条件可以吗不行 templateclass T, class ...Args void ShowList(T value, Args... args) {cout value ;if (sizeof...(args)){return;}ShowList(args...); } 函数模板并不能调用函数模板需要在编译时根据传入的实参类型进行推演生成对应的函数这个生成的函数才能够被调用。 而这个推演过程是在编译时进行的当推演到参数包args中参数个数为0时还需要将当前函数推演完毕这时就会继续推演传入0个参数时的ShowList函数此时就会产生报错因为ShowList函数要求至少传入一个参数。 这里编写的if判断是在代码编译结束后运行代码时才会所走的逻辑也就是运行时逻辑而函数模板的推演是一个编译时逻辑。 还有一种特殊的方式该方法比较抽象就是使用逗号表达式展开参数包 -3. 逗号表达式展开参数包 templateclass T void CPPprint(const T value) {cout value ; }templateclass ...Args void ShowList(Args... args) {int array[] {( CPPprint(args), 0)...};cout endl; } 当我们在数组中不标注元素个数时编译器会帮我们自动推导元素个数这时它会帮我们展开参数包 如下 int array[] {( CPPprint(args), 0), CPPprint(args), 0), CPPprint(args), 0), CPPprint(args), 0)}; 在调用CPPprint函数的同时利用逗号运算符的特性进行对数组的初始化 其实也可以不使用逗号运算符完成该操作 templateclass T int CPPprint(const T value) {cout value ;return 0; }templateclass ...Args void ShowList(Args... args) {int array[] { (CPPprint(args))... };cout endl; } 将被调用的函数设置一个返回值调用之后返回0这样就可以在编译器展开参数包调用函数时通过返回值初始化 STL中的emplace相关接口函数 以便大家更好的理解emplace先给大家看一段代码可变模板参数的使用场景 class Date { public:Date(int year 1, int month 1, int day 1):_year(year), _month(month), _day(day){cout Data()~构造函数 endl;}Date(const Date d):_year(d._year), _month(d._month), _day(d._day){cout Date()~拷贝构造 endl;}private:int _year;int _month;int _day; };templateclass ...Args Date* Init(Args... args) {Date* ret new Date(args...);return ret; }int main() {Date* p1 Init();Date* p2 Init(2024);Date* p3 Init(2024, 11);Date* p4 Init(2024, 11, 12);Date d1(2, 3, 3);Date* p5 Init(d1);return 0; } 我们通过将参数传入参数包在编译期间通过将参数包展开的操作进行对象的构造 STL容器中emplace相关插入接口函数 C11标准STL中的容器增加emplace版本的插入接口比如list容器的push_frontpush_back和insert函数都增加了对应的emplace_frontemplace_backemplace函数。如下 emplace接口全部都是使用的可变参数模板 注意两个是万能引用并不是右值引用 对比list中的push_back和emplace_back对于emplace系列接口而言它的主要优势就是直接在容器内部构造元素可以结合我上面给的场景进行理解而不是构造一个临时对象在复制或移动到容器中可以有效的避免拷贝和移动操作 以emplace和push_back为例 调用push_back函数插入元素时可以传入左值对象或者右值对象也可以使用列表初始化 调用emplace时可以传左值对象或者右值对象但是不能使用列表初始化emplace系列最大的特点就是插入元素时可以传入用于构造元素的参数包 比如 int main() {listpairnxbw::string, int mylist;pairnxbw::string, int kv(nxbw, 10);mylist.emplace_back(kv); //传左值mylist.emplace_back(make_pair(nxbw, 10)); //传右值mylist.emplace_back(nxbw, 10); //传参数包mylist.push_back(kv); //传左值mylist.push_back(make_pair(nxbw, 10)); //传右值mylist.push_back({ nxbw, 10 }); //使用列表初始化return 0; } 原地构造使用emplace你可以提供构造元素所需的参数容器会直接在emplace接口的实现中构造该对象 emplace系列接口的工作流程 emplace系列接口的工作流程如下 先通过空间配置器为新结点获取一块内存空间注意这里只会开辟空间不会自动调用构造函数对这块空间进行初始化。然后调用allocator_traits::construct函数对这块空间进行初始化调用该函数时会传入这块空间的地址和用户传入的参数需要经过完美转发。在allocator_traits::construct函数中会使用定位new表达式显示调用构造函数对这块空间进行初始化调用构造函数时会传入用户传入的参数需要经过完美转发。将初始化好的新结点插入到对应的数据结构当中比如list容器就是将新结点插入到底层的双链表中。 emplace系列接口的意义 由于emplace系列接口的可变模板参数的类型都是万能引用因此既可以接收左值对象也可以接收右值对象还可以接收参数包。 如果调用emplace系列接口时传入的是左值对象那么首先需要先在此之前调用构造函数实例化出一个左值对象最终在使用定位new表达式调用构造函数对空间进行初始化时会匹配到拷贝构造函数。如果调用emplace系列接口时传入的是右值对象那么就需要在此之前调用构造函数实例化出一个右值对象最终在使用定位new表达式调用构造函数对空间进行初始化时就会匹配到移动构造函数。如果调用emplace系列接口时传入的是参数包那就可以直接调用函数进行插入并且最终在使用定位new表达式调用构造函数对空间进行初始化时匹配到的是构造函数。 总结一下 传入左值对象需要调用构造函数拷贝构造函数。传入右值对象需要调用构造函数移动构造函数。传入参数包只需要调用构造函数。 当然这里的前提是容器中存储的元素所对应的类是一个需要深拷贝的类并且该类实现了移动构造函数。否则在调用emplace系列接口时传入左值对象和传入右值对象的效果都是一样的都需要调用一次构造函数和一次拷贝构造函数。 实际emplace系列接口的一部分功能和原有各个容器插入接口是重叠的因为容器原有的push_back、push_front和insert函数也提供了右值引用版本的接口如果调用这些接口时如果传入的是右值对象那么最终也是会调用对应的移动构造函数进行资源的移动的。 emplace接口的意义 emplace系列接口最大的特点就是支持传入参数包用这些参数包直接构造出对象这样就能减少一次拷贝这就是为什么有人说emplace系列接口更高效的原因。 但emplace系列接口并不是在所有场景下都比原有的插入接口高效如果传入的是左值对象或右值对象那么emplace系列接口的效率其实和原有的插入接口的效率是一样的。 emplace系列接口真正高效的情况是传入参数包的时候直接通过参数包构造出对象避免了中途的一次拷贝。   通过下面的场景我们来验证一下 namespace nxbw {class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str _size;}string(const char* str ):_size(strlen(str)), _capacity(_size){cout string(char* str) endl;_str new char[_capacity 1];strcpy(_str, str);}// s1.swap(s2)void swap(string s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string s):_str(nullptr){cout string(const string s) -- 深拷贝 endl;string tmp(s._str);swap(tmp);}// 赋值重载string operator(const string s){cout string operator(string s) -- 深拷贝 endl;string tmp(s);swap(tmp);return *this;}// 移动构造string(string s):_str(nullptr), _size(0), _capacity(0){cout string(string s) -- 移动语义 endl;swap(s);}// 移动赋值string operator(string s){cout string operator(string s) -- 移动语义 endl;swap(s);return *this;}~string(){delete[] _str;_str nullptr;}char operator[](size_t pos){assert(pos _size);return _str[pos];}void reserve(size_t n){if (n _capacity){char* tmp new char[n 1];strcpy(tmp, _str);delete[] _str;_str tmp;_capacity n;}}void push_back(char ch){if (_size _capacity){size_t newcapacity _capacity 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] ch;_size;_str[_size] \0;}//string operator(char ch)string operator(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity; // 不包含最后做标识的\0}; } int main() {listpairnxbw::string, int mylist;pairnxbw::string, int kv(nxbw, 10); //构造mylist.emplace_back(kv); //传左值mylist.emplace_back(pairnxbw::string, int(nxbw, 10)); //传右值mylist.emplace_back(nxbw, 10); //传参数包return 0; } 由于我们在string的构造函数、拷贝构造函数和移动构造函数当中均打印了一条提示语句因此我们可以通过控制台输出来判断这些函数是否被调用。 下面我们用一个容器来存储模拟实现的string并以不同的传参形式调用emplace系列函数。比如 说明一下 模拟实现string的拷贝构造函数时复用了构造函数因此在调用string拷贝构造的后面会紧跟着调用一次构造函数。 为了更好的体现出参数包的概念因此这里list容器中存储的元素类型是pair我们是通过观察string对象的处理过程来判断pair的处理过程的。 这里也可以以不同的传参方式调用push_back函数顺便验证一下容器原有的插入函数的执行逻辑。比如 int main() {listpairnxbw::string, int mylist;pairnxbw::string, int kv(nxbw, 10);mylist.push_back(kv); //传左值mylist.push_back(pairnxbw::string, int(nxbw, 10)); //传右值mylist.push_back({ nxbw, 10 }); //使用列表初始化return 0; } 模拟实现emplace接口 namespace nxbw {// 模拟实现list在之前的章节有提过这里只是将原来的代码多增加一些接口的片段代码// 这是list需要用到的节点类templateclass Tstruct __list_node{__list_node(const T val T()):_data(val), _prev(nullptr), _next(nullptr){}// 这里需要在原来的基础上需要增加一个可变模板参数模板的构造函数,方便下面使用newtemplateclass ...Args__list_node(Args ...args): _data(std::forwardArgs(args)...), _prev(nullptr), _next(nullptr){}T _data;__list_node* _prev;__list_node* _next;};templateclass Tstruct list{templateclass ...Argsiterator emplace(iterator position, Args... args){node* cur position._node;node* prev cur-_prev;// 函数参数包的完美转发node* newnode new node(forwardArgs(args)...);prev-_next newnode;newnode-_prev prev;newnode-_next cur;cur-_prev newnode;return iterator(cur);}templateclass ...Argsvoid emplace_back(Args... args){// 函数参数包的完美转发emplace(end(), forwardArgs(args)...);}// 获取节点函数,这里更新成了万能引用版的templateclass Tnode* get_node(T val T()){node* new_node new node(forwardT(val)); // 完美转发new_node-_prev new_node;new_node-_next new_node;return new_node;}private:__list_nodeT* _head; // 指向节点类的指针}; }; emplace系列和push_back以及insert的区别 效率方面对于左值引用版本的push_back和insert来说确实有很大的效率提升对于右值引用版本的push_back和insert来说效率其实差不多因为移动赋值/拷贝代价足够小 构造复杂对象当元素的构造比叫复杂时emplace可以让代码更简洁直接传入构造参数即可
http://www.pierceye.com/news/873261/

相关文章:

  • 公司网站做一下多少钱最吉利旺财的公司名字
  • 网站建设维护及使用管理办法营销策划的步骤
  • 优秀网站设计案例在家开个人工作室违法吗
  • 腾讯云建设网站wordpress仿知乎社区
  • 《网站开发技术》模板linchong.wordpress
  • 找做企业网站论文旅游网站建设
  • 类似情侣空间的网站开发seo外推软件
  • 网站建设策划方案怎么写工业品网络营销
  • 上海本地网站建设微信网站怎么建设
  • 江苏华江建设集团网站wordpress开发找工作
  • 家政服务网站源码自己做网站好还是让别人做
  • 手机网站用什么系统做网站在什么地方发帖子呢
  • 虚拟电脑可以做网站吗中国建设行业信息网站
  • 网站设计建设合同公司网页设计实例教程
  • 仿起点小说网站开发网站图片优化工具
  • 在线做logo的网站泉州做网站哪家好
  • 知名企业网站人才招聘情况如何网络系统集成
  • 做灯带的网站重庆有哪些好玩的地方
  • 小孩子做手工做游戏的网站百度账号设置
  • 大庆做网站公司巩义网站建设方案报价
  • 该网站受海外服务器保护品牌营销型网站建设公司
  • 免费做一建或二建题目的网站郑州企业建站系统模板
  • 想自己建个网站徐州做网站软件
  • 蓝色系网站设计企业应对承包商的施工方案尤其是
  • 旅游网站 源码 织梦导购网站开发
  • 头像制作网站开源低代码平台
  • 网站到期域名怎么解决办法自己动手建立网站3
  • 比较有名的网站建设平台吉林建设网站
  • 网站服务器解决方案wamp安装wordpress
  • 义乌制作网站赣州网站建设公司