当前位置: 首页 > 编程日记 > 正文

C++模板详解

参考:C++ 模板详解(一)

模板类型进行参数化的工具;通常有两种形式: 

  • 函数模板:仅参数类型不同;
  • 类模板:   仅数据成员成员函数类型不同。

目的:让程序员编写与类型无关的代码。

注意:模板的声明或定义只能在全局、命名空间、类范围内进行。即不能在局部范围、函数内进行,比如不能在main函数中声明或定义一个模板。

一 函数模板

1 函数模板的格式: 

template <class 类型名1,class 类型名2,......> 
返回类型 函数名(参数列表)
{函数体
}
  • template、class是关键字,class可以用关键字typename代替,在这里typename和class没区别
  • <>中的参数叫模板形参不能为空。模板形参用传入的实参类型来初始化。
  • 模板形参可以在 返回类型、参数列表、函数体内使用。一旦编译器确定了模板实参类型,就称他实例化了函数模板的一个实例。

例1:swap函数的模板:

template <class T> 
void swap(T& a, T& b)   //参数列表使用模板形参
{
T tmp = a; //函数体内使用模板形参
a = b;
b = tmp; }
  • 当调用这个函数模板时,类型T就会被被调用时的类型所代替
  • 比如swap(a,b),其中abint 型,模板函数就变为swap(int &a, int &b)
  • 而当swap(c,d),其中cddouble型时,模板函数会被替换为swap(double &a, double &b)。

例2:max函数的模板

 1 #include<iostream>
 2 
 3 template<typename T>
 4 const T& myMax(const T &a, const T &b)
 5 {
 6   return a > b ? a : b;
 7 }
 8  
 9 int main()
10 {
11   cout << myMax(2.1, 2.2) << endl;         //输出2.2  模板实参被隐式推演成double
12   cout << myMax<double>(2.1, 2.2) << endl; //输出2.2  显示指定模板参数。
13   cout << myMax<int>(2.1, 2.2) << endl;    //输出2    显示指定的模板参数,会将参数转换为int。
14 
15   return 0;
16 }

2、注意:

  • 不存在swap(int, int)这样的调用! 即不能用类型初始化,只能用实参推演来进行。即根据2来推出int型。
  • 即只能进行swap(2, 3);  或者  int a, b; swap(a,b);   这样的调用。

二 类模板

1 类模板的格式为:

template<class 形参名1, class 形参名2, ...>
class 类名
{... 
};
  • 与函数模板一样,以template开始,后接模板形参列表模板形参不能为空
  • 类的数据成员和函数成员可以使用模板形参。

:一个类模板的例子:

template<class T> 
class A
{
public: T a;               //类成员使用模板形参T b; T func(T c, T &d);
};

 

2 类模板对象的创建

  • 方法:A<int> m;  类A中用到模板形参的地方都会被int 所代替。
  • 两个模板形参:A<int, double> m;  类型之间用逗号隔开。

3 类模板形参必须指定类型而不是实参:

  • 必须这样指定 A<int> m;  明确指定类型。
  • 不能这样使用A<2> m;  类模板形参不存在实参推演的问题。

4 在类模板外部定义成员函数的方法为

template<模板形参列表> 
函数返回类型 类名<模板形参名>::函数名(参数列表) 
{函数体
}

:比如模板类A,有两个模板形参T1,T2,有一个成员函数 void func(),则外部定义该函数的语法为:

template<class T1, class T2>   //与类一致
void A<T1, T2>::func()      //类名也要加上模板参数列表
{
}

注意:当在类外面定义类的成员时,template后面的模板形参应与所定义的类的模板形参一致

三 模板的形参

包括 类型形参、非类型形参、默认形参。

1 类型形参

类型形参由关键字class或typename后接说明符构成,如

template<class T> 
void func(T a)
{
};
  • 其中 就是一个类型形参,名字由用户确定。

函数模板,同一个类型形参,必须用相同类型的实参来初始化,比如

template<class T>
void func(T a, T b)
{
}
  • 调用 func(2, 3.2); 将编译出错,因为模板形参T同时被指定为int 和 double,不一致,会出错。

类模板,其内部成员函数,则没有上述的限制,比如

template<class T>
class A
{
public:T func(T a, T b);   //或者T func(const T &a, const T &b);  普通引用会编译报错
};
  • 声明 A<int> a;  调用 a.func(2, 3.2);  在编译时不会出错
  • 第二个实参3.2类型为double,在运行时,会强制类型转换3

:模板类的对象调用成员函数:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 template<class T>
 5 class A
 6 {
 7 public:
 8     A();
 9     T func(T a, T b);
10 };
11 
12 template<class T>     //类外定义构造函数
13 A<T>::A()
14 {
15 }
16 
17 template<class T>     //类外定义成员函数
18 T A<T>::func(T a, T b)
19 {
20     return a + b;
21 }
22 
23 int main(int argc, char *argv[])
24 {
25     A<int> ia;                        //模板实参为int类型的对象
26     cout << ia.func(3, 2.1) << endl;  //输出5
27 
28     A<double> da;
29     cout << da.func(3, 2.1) << endl;  //输出5.1
30 
31     return 0;
32 }

 

2 非类型形参

也就是内置类型形参,如

template<class T, int a>   //int a 就是非类型形参
class B
{
};

非类型形参有几点要注意的:

  • 在模板定义的内部是常量值,也就是说,上面的a在类B内部是一个常量。
  • 形参只能是整型、指针、引用,像double、string、string **是不允许的,但是double &、double *、对象的引用或指针是正确的。
  • 实参必须是一个常量表达式,即必须能在编译时计算出结果。注意局部对象/变量和其地址,全局指针/变量/对象,都不是常量表达式;全局变量/对象地址或引用const类型变量sizeof的结果,都是常量表达式。
  • 形参是整型时,实参也必须是整型的,且在编译期间是常量,比如
template <class T, int a> 
class A
{
};

如果有int b;  这时 A<int, b> m; 出错,因为b不是常量;如果有 const int b;  这时 A<int, b> m;  正确,因为这时b是常量。

  • 非类型形参一般不应用于函数模板中,比如有函数模板
template<class T, int a> 
void func(T b)
{
}

若用func(2)调用,会出现无法为非类型形参a推演出参数的错误;可以用显示模板实参来解决,如用func<int, 3>(2); 把非类型形参a设置为整数3。

  • 形参实参间所允许的转换 
//1 数组到指针,函数到指针的转换
template<int *a>
class A { };
int b[10];
A<b> m;      //数组转换成指针 //2 const修饰符的转换
template<const int *a>
class A { };
int b;
A<&b> m;     //从int*转换成const int *//3 提升转换
template<int a>
class A { };
const short b = 2;
A<b> m;       //short到int提升//4 整数转换
template<unsigned int a>
class A { };
A<3> m;    //int到unsigned int转换//5 常规转换

由用户指定栈的大小,并实现栈的相关操作

  1 #include <iostream> 
  2 #include <string> 
  3 #include <stdexcept>  //std::out_of_range
  4 #include <cstdlib>    //EXIT_FAILURE
  5 using namespace std;
  6 
  7 /*********模板类,声明开始,一般都是放在头文件的*********/
  8 
  9 template<class T, int MAXSIZE>
 10 class myStack
 11 {
 12 public:
 13     myStack();
 14     void push(T const &);  //入栈
 15     void pop();            //出栈
 16     T    top() const;      //返回栈顶
 17 
 18     bool empty() const     //判断是否为空
 19     {
 20         return size == 0;
 21     }
 22 
 23     bool full() const      //判断栈是否已满
 24     {
 25         return size == MAXSIZE;
 26     }
 27 
 28 private:
 29     T   elems[MAXSIZE];    //使用数组存放栈元素,由于非类型形参MAXSIZE在类内是一个常量,所以可以用来声明数组大小
 30     int size;              //栈已使用空间
 31 };
 32 
 33 /**********模板类,声明结束,定义成员函数开始********/
 34 
 35 template<class T, int MAXSIZE>
 36 myStack<T, MAXSIZE>::myStack(): size(0)      //构造函数,初始化为空
 37 {
 38 }
 39 
 40 template<class T, int MAXSIZE>
 41 void myStack<T, MAXSIZE>::push(T const &new_elem)     //入栈
 42 {
 43     if(size == MAXSIZE)
 44     {
 45         throw out_of_range("myStack::push(): stack is full");
 46     }
 47     elems[size++] = new_elem;
 48 }
 49 
 50 template<class T, int MAXSIZE>
 51 void myStack<T, MAXSIZE>::pop()       //栈顶出栈
 52 {
 53     if(size <= 0)
 54     {
 55         throw out_of_range("myStack::pop(): stack is empty");
 56     }
 57     --size;
 58 }
 59 
 60 template<class T, int MAXSIZE>
 61 T myStack<T, MAXSIZE>::top() const    //返回栈顶元素
 62 {
 63     if(size <= 0)
 64     {
 65         throw out_of_range("myStack::top(): stack is empty");
 66     }
 67     return elems[size - 1];
 68 }
 69 
 70 /***********成员函数定义结束**********************/
 71 
 72 int main(int argc, char *argv[])
 73 {
 74     try
 75     {
 76         myStack<int,    20>  int20Stack;    //显示模板实参
 77         myStack<int,    40>  int40Stack;
 78         myStack<string, 40>  stringStack;
 79 
 80         int20Stack.push(7);
 81         cout << int20Stack.top() << endl;   //输出7
 82         int20Stack.pop();
 83 
 84         for(int i = 0; i < 40; ++i)
 85             int40Stack.push(i);
 86         cout << int40Stack.top() << endl;   //输出39
 87         //int40Stack.push(41);              //继续添加元素,会抛出异常,输出Exception: myStack::push(): stack is full
 88         
 89         stringStack.push("hello");
 90         cout << stringStack.top() << endl;  //输出hello
 91         stringStack.pop();
 92         //stringStack.pop();                //继续出栈,会抛出异常,输出Exception: myStack::push(): stack is empty
 93 
 94         return 0;
 95     }
 96     catch(out_of_range const &ex)
 97     {
 98         cerr << "Exception: " << ex.what() << endl;
 99         return EXIT_FAILURE;
100     }
101 }

3 默认形参

类模板可以有默认值,函数模板不能有默认值。

2 类模板,类型形参,默认值形式为:

template<class T1, class T2 = int>   //为第二个模板类型形参提供int型的默认值
class A
{
};

3 类模板,类型形参,默认值和普通函数的默认参数一样,如果有多个类型形参,则从第一个设定了默认值的形参之后,所有模板形参都要设定默认值

template<class T1 = int, class T2>  //错误!如果T1有默认值,T2也必须有
class A
{
};

4 类模板,类型形参,外部定义类的成员函数。template 后的形参列表应省略掉默认值。

template<class  T1, class T2 = int> 
class A
{
public: void func();
}; template<class T1,class T2>   //定义方法,省略掉默认值
void A<T1,T2>::func()
{
}

:有默认值的类模板

 1 #include <iostream>
 2 using namespace std; 
 3 
 4 template<typename T1, typename T2 = double, int abc = 5>  //第二个类型形参和非类型形参有默认值
 5 class A
 6 {
 7 public:
 8     void print(T1 a, T2 b);
 9 };
10 
11 template<typename T1, typename T2, int abc>   //类外定义时不能有默认值,毕竟类的声明是作为接口给别人看的
12 void A<T1, T2, abc>::print(T1 a, T2 b)
13 {
14     cout << a << ' ' << b << endl;
15     cout << abc << endl;16 }
17 
18 int main(int argc, char *argv[])
19 {
20     A<int> a;
21     a.print(2.2, 2.1);   //输出 2 2.1 5, 输出2是因为指定为int型,进行了类型转换,输出5是默认值
22 
23     return 0;
24 }

转载于:https://www.cnblogs.com/qieerbushejinshikelou/p/3961964.html

相关文章:

autocad2007二维图画法_cad怎样绘制简单的二维图形

CAD绘制二维图形非常的简单&#xff0c;大家经常用它来画图&#xff0c;下面是学习啦小编带来关于cad怎样绘制简单的二维图形的内容&#xff0c;希望可以让大家有所收获!cad绘制简单二维图形的方法1、绘图菜单绘图菜单是绘制图形最基本、最常用的方法&#xff0c;其中包含了Aut…

MyEclipse 中配置struts2.2.1的方法

MyEclipse中配置Struts2.2.1版本基本步骤&#xff1a;1&#xff0c;首先就是要建立一个web project项目2&#xff0c;设置jdk和servers路径&#xff0c;如果jdk和servers已经配置ok&#xff0c;跳过这一步骤。在菜单中的window选项中配置jdk和servers对于jdk&#xff0c;点击ja…

BZOJ.5249.[九省联考2018]iiidx(贪心 线段树)

BZOJLOJ洛谷 \(d_i\)不同就不用说了&#xff0c;建出树来\(DFS\)一遍。 对于\(d_i\)不同的情况&#xff1a; Solution 1&#xff1a;xxy tql! 考虑如何把这些数依次填到树里。 首先对于已解锁的节点\(x\)&#xff08;已解锁是指父节点已经处理完的点&#xff0c;刚开始就是\(fa…

leetcode-376 摆动序列

如果连续数字之间的差严格地在正数和负数之间交替&#xff0c;则数字序列称为摆动序列。第一个差&#xff08;如果存在的话&#xff09;可能是正数或负数。少于两个元素的序列也是摆动序列。 例如&#xff0c; [1,7,4,9,2,5] 是一个摆动序列&#xff0c;因为差值 (6,-3,5,-7,3…

bootstrap3中关于布局的两种样式

container&#xff1a;用.container包裹的内容即可实现居中对齐。注意&#xff0c;由于在各分辨率下面都设置了padding 和 固定宽度&#xff0c;.container不能嵌套。row&#xff1a;栏栅系统是把父容器平均分为12列。注意&#xff0c;row可以被嵌套。 通过下表可以详细查看Boo…

adg oracle 架构_云化双活的架构演进,宁夏银行新核心搭载Oracle 19c投产上线

云和恩墨顺利完成宁夏银行新数据中心数据库平台的建设&#xff0c;包括新数据中心RAC搭建、DG搭建、旧数据中心到新数据中心的数据迁移&#xff0c;以及在整个项目生命周期中的实施规范、性能测试保障、压力测试等。6月12日&#xff0c;宁夏银行数据库完成全部迁移&#xff0c;…

MFC里ON_COMMAND_RANGE消息映射的ID问题

今天在工作中遇到一个问题&#xff0c;一个动态菜单&#xff0c;每个菜单的菜单项ID是我自己定义的&#xff0c;定义如下&#xff1a; #define IDM_SEARCHRECORD0 222240 #define IDM_SEARCHRECORD1 222241 #define IDM_SEARCHRECORD2 222242 #define IDM_SEARCHRECORD3 …

反射拷贝对象的思路:

0 根据构造器创建对象 1.获取传入进来的对象的字段 2.获取字段的类型 3.拼接 set 与get方法 4 获取传入进来的对象的值 并设置给新对象转载于:https://www.cnblogs.com/classmethond/p/10362263.html

leetcode-402 移掉K位数组

给定一个以字符串表示的非负整数 num&#xff0c;移除这个数中的 k 位数字&#xff0c;使得剩下的数字最小。 注意: num 的长度小于 10002 且 ≥ k。 num 不会包含任何前导零。 示例 1 : 输入: num “1432219”, k 3 输出: “1219” 解释: 移除掉三个数字 4, 3, 和 2形成一…

c++ using 前置声明_C++ 类的前置声明

今天在研究C”接口与实现分离“的时候遇到了一个问题&#xff0c;看似很小&#xff0c;然后背后的东西确值得让人深思&#xff01;感觉在学习的过程中有太多的为什么&#xff0c;而每一个为什么背后都隐藏着一些原理和目的&#xff0c;所以得多问自己”为什么“&#xff0c;这样…

测试用的序列化方法

对于实体&#xff0c;进行底层方法测试的时候&#xff0c;经常逐一赋值很麻烦&#xff0c;网上找到序列化xml方法&#xff0c;感觉挺好用的。 前端调用方法时&#xff0c;将实体序列化写入xml文件 //xml路径string filePath "D:\1.xml";using (System.IO.StreamWrit…

HighChart学习-更新数据data Series与重绘

一&#xff1a;HighChart介绍 基于JQuery的纯JavaScript的图标库&#xff0c;支持各种图表显示&#xff0c;同时还支持Mootools 与Prototype详细版本支持在这里&#xff1a; JQuery 1.3.2 - 1.9.x. 2.0.x for modern browsers Mootools 1.2.5 - 1.4.5 Prototype 1.7 支持目…

shell代码模板

批量ssh登录机器#site_search_hosts 10.4.16.205,10.4.20.87,10.4.20.88,10.4.20.89,10.4.20.90,10.4.20.92,10.4.20.93,10.4.21.51,10.4.21.52,10.4.21.53,10.4.21.54,10.4.33.136,10.4.33.137,10.4.33.138,10.4.33.139,10.4.33.140site_search_hosts10.4.16.205,10.4.20.87,1…

leetcode-55 跳跃游戏

给定一个非负整数数组&#xff0c;你最初位于数组的第一个位置。 数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个位置。 示例 1: 输入: [2,3,1,1,4] 输出: true 解释: 我们可以先跳 1 步&#xff0c;从位置 0 到达 位置 1, 然后再从位置 1…

子分类账知识学习(汇总网上比较有用的资料)

子模块和GL之间关联的变化 12i在功能模块上的变化很多&#xff0c;比如&#xff0c;基本每个模块都启用了MOAC特性&#xff0c;新增加了子帐模块&#xff0c;税模块等等很多新的模块&#xff0c;OPM库存和离散库存集成了。不过这些变化中&#xff0c;大部分不是我们需要重点…

zynqpl端时钟_第十一章 ZYNQ-MIZ701 PS读写PL端BRAM

本篇文章目的是使用Block Memory进行PS和PL的数据交互或者数据共享&#xff0c;通过zynq PS端的Master GP0端口向BRAM写数据&#xff0c;然后再通过PS端的Mater GP1把数据读出来&#xff0c;将结果打印输出到串口终端显示。涉及到AXI BRAM Controller 和 Block Memery Generato…

nagios报警的问题

最近我写了关于naigos监控的安装与配置的技术文档&#xff0c;公司运维按照我的文档部署naigos&#xff0c;发现不能发送报警邮件&#xff0c;经过我的检查&#xff0c;发现问题如下&#xff1a;1、hosts里的配置[rootnagios ~]# cat /etc/hosts # Do not remove the followin…

机器学习常见的分类算法的优缺点

1. 前言 在机器学习中&#xff0c;种类最多的一类算法要属很类算法&#xff0c;本文对机器学习中的各种分类算法的优缺点做一个总结。 2. 贝叶斯分类法 2.1 优点 所需估计的参数少&#xff0c;对于缺失数据不敏感。有着坚实的数学基础&#xff0c;以及稳定的分类效率。2.2 缺点…

公司新来一个同事:为什么 HashMap 不能一边遍历一边删除?一下子把我问懵了!

前段时间,同事在代码中KW扫描的时候出现这样一条:上面出现这样的原因是在使用foreach对HashMap进行遍历时,同时进行put赋值操作会有问题,异常ConcurrentModificationException。于是帮同简单的看了一下,印象中集合类在进行遍历时同时进行删除或者添加操作时需要谨慎,一般使用迭代器进行操作。于是告诉同事,应该使用迭代器Iterator来对集合元素进行操作。同事问我为什么?这一下子把我问蒙了?对啊,只是记得这样用不可以,但是好像自己从来没有细究过为什么?

一文搞懂MySQL索引

官方介绍索引是帮助MySQL高效获取数据的数据结构。更通俗的说,数据库索引好比是一本书前面的目录,能加快数据库的查询速度。一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往是存储在磁盘上的文件中的(可能存储在单独的索引文件中,也可能和数据一起存储在数据文件中)。我们通常所说的索引,包括聚集索引、覆盖索引、组合索引、前缀索引、唯一索引等,没有特别说明,默认都是使用B+树结构组织(多路搜索树,并不一定是二叉的)的索引。看到这里,你是不是对于自己的sql语句里面的索引的有了更多优化想法呢。

leetcode-45 跳跃游戏II

给定一个非负整数数组&#xff0c;你最初位于数组的第一个位置。 数组中的每个元素代表你在该位置可以跳跃的最大长度。 你的目标是使用最少的跳跃次数到达数组的最后一个位置。 示例: 输入: [2,3,1,1,4] 输出: 2 解释: 跳到最后一个位置的最小跳跃数是 2。 从下标为 0 跳到下标…

做技术到底可以做到哪种地步-技术为什么越走越窄 (转)

尽管做技术已经有不少年头了&#xff0c;不管是犹犹豫豫还是坚定不移&#xff0c;我们走到了现在&#xff0c;依然走在技术这条路上。 不管我们处于何种职位&#xff0c;拿着哪种薪水&#xff0c;其实&#xff0c;我们会是不是的问问自己“做技术到底可以做到那种地步”&#x…

linux本地agent执行脚本_github 4.4K星|马哥教育企业教练团队研发一款轻量级、无Agent自动化运维平台...

马哥教育企业教练团队研发了一款自动化运维平台系统—Spug&#xff0c;上线后广受中小运维爱好者喜爱&#xff0c;目前github4.4k星&#xff0c;已经成为自动化热门项目。2020年了&#xff0c;运维不会搞运维自动化&#xff0c;都不好意思说自己做运维的了&#xff0c;大一点的…

mysql 数据目录更改

[CentOS]MySQL更改数据文件存储目录环境&#xff1a;CentOS(Linux) Mysql5.X 1.如果MySQL已经启动的话&#xff0c;需要先停止MySQL的运行#service mysqld stop2.home 目录下新建目录[data]/home #mkdir data3.移动MySQL默认数据库文件#mv /var/lib/mysql /home/data4.修改MySQ…

leetcode-452 用最少数量的箭引爆气球

在二维空间中有许多球形的气球。对于每个气球&#xff0c;提供的输入是水平方向上&#xff0c;气球直径的开始和结束坐标。由于它是水平的&#xff0c;所以y坐标并不重要&#xff0c;因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。平面内最多存在104个气球…

ssm 异常捕获 统一处理_SpringMVC 统一异常处理介绍及实战

背景什么是统一异常处理目标统一异常处理实战用 Assert(断言) 替换 throw exception定义统一异常处理器类扩展总结《Java 2019 超神之路》《Dubbo 实现原理与源码解析 —— 精品合集》《Spring 实现原理与源码解析 —— 精品合集》《MyBatis 实现原理与源码解析 —— 精品合集》…

【iOS开发】企业版证书($299)In-House方式发布指南 (转)

一、明确几个概念 1、企业版IDP&#xff1a;即iOS Development Enterprise Program。注意是$299&#xff0f;Year那种&#xff0c;并不是$99/Year的那种。 2、In House&#xff1a;是只企业内部发布&#xff0c;仅限企业内部人员使用。 二、In-House方式特点 1、不能发布到Appl…

苹果所有常用证书,appID,Provisioning Profiles配置说明及制作图文教程(精)

概述&#xff1a; 苹果的证书繁锁复杂&#xff0c;制作管理相当麻烦&#xff0c;今天决定重置一个游戏项目中的所有证书&#xff0c;做了这么多次还是感觉很纠结&#xff0c;索性直接记录下来&#xff0c;日后你我他查阅都方便&#xff1b; 首先得描述一下各个证书的定位&#…

ES6语法~解构赋值、箭头函数、class类继承及属性方法、map、set、symbol、rest、new.target、 Object.entries......

2015年6月17日 ECMAScript 6发布正式版本 前面介绍基本语法, 后面为class用法及属性方法、set、symbol、rest等语法. 一、基本语法: 1、 定义变量&#xff1a;let 使用var 定义的变量没有{ }限制&#xff0c;在条件中定义的i&#xff0c;全局中都可以使用&#xff0c…

读书:历史 -- 东印度公司

浅田实 — 日本 文学博士、英国近世史学专家 东印度公司曾经为英国殖民印度&#xff0c;扮演过冲锋陷阵的历史角色。 富可敌国来形容东印度公司绰绰有余&#xff0c;它的崛起和衰落是时代变迁的缩影。 英国发起的第一次工业革命迫切需要资本的输入和输出来带动蒸汽齿轮得高速转…