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

信用泰安网站顺网网页游戏大厅

信用泰安网站,顺网网页游戏大厅,房产中介在线咨询,网站制作应用知识前言 本期我们接着继续介绍C11的新特性,本期我们介绍的这个新特性是很多人都感觉抽象的语法!它就是可变的模板参数! 目录 前言 一、可变的模板参数 1.1可变的参数列表 1.2可变的参数包 1.3可变参数包的解析 • 递归展开解析 • 逗号…

前言

本期我们接着继续介绍C++11的新特性,本期我们介绍的这个新特性是很多人都感觉抽象的语法!它就是可变的模板参数!

目录

前言

一、可变的模板参数

1.1可变的参数列表

1.2可变的参数包

1.3可变参数包的解析

• 递归展开解析

• 逗号表达式解析

二、emplace系列接口


一、可变的模板参数

1.1可变的参数列表

对于可变参数列表,我们在C语言就见过!例如最典型的就是 printf / scanf 了!

我们在使用的时候,需要指定类型将不同类型/数量的类型都可以传递过去!

int main() 
{int a;double b;char c;printf("请输入一个整数、一个浮点数和一个字符: ");scanf("%d %lf %c", &a, &b, &c);printf("输入了: %d %lf %c\n", a, b, c);return 0;
}

1.2可变的参数包

C++98/03,类模板和函数模板中只能含固定的模板参数,C++11引入了可变的模板参数能够让你创建接受可变参数的函数模板和类模板!这无疑是一个巨大的改进!正是改变巨大,所以这块比较抽象,也比较晦涩难懂!本博客主要介绍的是函数模板的可变参数!

先来见一见基本的可变参数的函数模板

template <class ...Args>
void ShowList(Args... args)
{// ...
}

此时,我们就可以给这个函数模板传递任意类型、任意个数的参数,把这个前面带 ... 的参数统称为参数包Args模板的参数包args函数形参的参数包(Args... args的个数是0~N

一般为了提高传递参数的效率,可变参数的类型一般会被写成,万能引用(引用折叠)的形式:

template <class ...Args>
void ShowList(Args&&... args)
{// ...
}

由于可变模板的参数是可变的,即支持任意类型、任意数量的参数,所以可以这样写:

int main()
{ShowList(1);ShowList(1, 'a', 9.9);ShowList(1, "aaa", 9.9, 'b');ShowList(std::vector<int>(), std::list<double>(), 'zzz', 999, "aaaaaaa");return 0;
}

这也体现了可变模板参数的强大!

1.3可变参数包的解析

用可变参数的人爽了,但是解析参数包的过程其实是不简单的!下面是一种非常典型的错误栗子:

template<class ...Args>
void showList(Args... args)
{// 错误的解析参数方式int n = sizeof...(args);for (int i = 0; i < n; i++){// 获取具体的可变参数args[i];}
}

这里使用sizeof获取参数包args的大小是把...放到了args的前面,第一次看起来的时候有点别扭~!(个人感觉)

这种方式是符合我们的直觉的,但是他是错的!

这里简单理解就是编译器不支持这样写!sizeof是编译时操作符,而参数包不是和数组一样连续的,所以不可用下标访问!

• 递归展开解析

void ShowList() { cout << endl; }// 用于结束递归template<class T, class ...Args>
void ShowList(const T& t, Args&& ...args)
{cout << t << " ";ShowList(args...);
}

这里他是如何做的呢?我用一个简单的例子,第三个来画一下:

也就是编译器,在编译期间将上述的函数模板实例化成了很多的实例,在运行时进行了调用

• 逗号表达式解析

除了上述递归的展开的方式解析参数包以外,还有一种直接展开的方式,就是用逗号表达式解析!具体如下:

template<class T>
void Print(T& t)
{cout << t << endl;
}template<class ...Args>
void ShowList(Args&&... args)
{int arr[] = { (Print(args), 0)... };cout << endl;
}

OK,编译后他就是这个样子:

template<class ...Args>
void ShowList(Args&&... args)
{int arr[] = { (Print(1), 0), (Print('a'), 0), (Print(9,9), 0)};cout << endl;
}

为什么这里要写成 (Print(args), 0)... 呢?

我们知道,逗号表达式的结果是最后一项的值!这是主要是在展开的同时给arr数组赋值!

二、emplace系列接口

C++11 也在STL中引入了可变的模板参数,这一批接口被称为emlpace系列的接口/函数!

这里以list为例,进行介绍!

为了方便演示,我们玩还是使用我们前几期用的cp::string,所以我们把他给拿过来:

namespace cp
{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};cp::string to_string(int value){cp::string str;bool flag = true;if (value < 0){flag = false;value = 0 - value;}while (value > 0){int x = value % 10;value /= 10;str += ('0' + x);}if (flag == false){str += '-';}std::reverse(str.begin(), str.end());return str;}
}
list<cp::string> lt;
// 分别让emplace_back和push_back都插入一个 左值 对象
cp::string s1("hello");
cout << "-------------------------------" << endl;
lt.push_back(s1);
cout << endl;
lt.emplace_back(s1);// 分别让emplace_back和push_back都插入一个 左值move后的 对象
cout << "-------------------------------" << endl;
lt.push_back(move(s1));
cout << endl;
lt.emplace_back(move(s1));// 分别让emplace_back和push_back都插入一个 右值 对象
cout << "-------------------------------" << endl;
lt.push_back(cp::string("6666")); 
cout << endl;
lt.emplace_back(cp::string("6666"));// 分别让emplace_back插入一个参数包   push_back都插入一个右值 对象
cout << "-------------------------------" << endl;
lt.push_back("6666");
cout << endl;
lt.emplace_back("6666");

看结果:

通过上面的栗子,我们可以得出结论:

1、当emplace和push系列都插入左值/move(左值)的结果是一样的(拷贝构造/移动构造)!

2、当emplace插入一个参数包,push系列都插入一个右值时,emplace是构造,而push系列是构造+移动构造!

• 为什么emplace系列传入参数包后是直接构造?

上面介绍了,emplace系列的是引用了模板的可变参数,所以他可以接受的是一个参数包!其实他的底层是一层层的传递下去,直接到构造那里,用参数包的参数直接构造的,所以就只有一次构造!

这其实就是,有人所说的emplace效率高的原因所在!就是直接使用emplace传递参数包可以少一次,移动构造!

所以,emplace系列的插入对象的优先级为

参数包 > 右值 > 左值

这里左值的代价较高,因为有一次的拷贝构造!右值其实和参数包的差别也还好,原因是移动构造的待见很轻!但是能用参数包的地方还是用参数包!以后就建议多使用emplace系列,因为emplace系列>=push系列 的效率!


OK,好兄弟,本期分享就到这里!我是cp我们下期再见~!

http://www.yayakq.cn/news/510829/

相关文章:

  • 网站建设维护要加班吗需要品牌网站建设
  • 网页设计公司企业文化山西seo优化
  • discuz网站ip中国能建官网
  • 河北建设官方网站梧州论坛最新主题
  • 网站后台无法审核合肥网站建设公司 招聘
  • 做网站一定要注册域名吗淘宝如何刷关键词增加权重
  • 做wap网站动漫设计与制作是什么
  • 河南省做网站的企业需要网站建设
  • 长寿做网站的电话橙色在网站中的应用
  • 免费国外医疗静态网站模板下载长沙网站公司
  • 建设门户公司网站做优惠券网站如何引流
  • 网页建站要多久服务器怎么设置ip做网站
  • 柯林wap建站程序个人版大连公司网站建设
  • 石家庄网站建设诚荐优度网络wordpress后台添加logo
  • 迅速百度网站自然排名如何快速找到做网站的客户
  • 浙江省建设工程监理协会网站教育网站建设 培训网站建设
  • 闽侯县网站建设wordpress登录攻击
  • 做网站需知cad软件
  • 网站名称图标如何做才能显示那有网页设计培训机构
  • 可以把网站生成软件品牌传播推广方案
  • 乐清网站改版公司深圳做网页
  • 网站开发前端跟后端的区别如何重新编辑wordpress
  • 门户网站开发合同自建网站公司
  • 企业商城网站建设方案网站pv统计方法
  • 建设电子商务网站的意义域名注册的流程
  • 主机屋网站搭建设置百度云虚拟主机
  • 品牌网站怎么建立为什么不用wordpress
  • scratch编程网站wordpress 管理权限
  • 网站做2微码网站建设工作汇报
  • 网站SEO的评价网站备案收费吗