c++类——模版(三)
本文最后更新于:1 年前
本文介绍模版的各种特殊情况:
¶(一)非类型模板参数(通常不应用于函数模版中):
-
模板的非类型形参是内置类型形参,模板参数不是一个类型而是一个具体的值,且值是常量表达式,因此调用非类型模板形参的实参必须是一个常量表达式(因此模板代码不能修改参数的值,也不能使用参数的地址)。
例如template<class T, int a> class B{}; 其中int a就是非类型的模板形参。
通常能被传入的常量表达式包括全局变量的地址或引用,全局对象的地址或引用const类型变量,sizeof的表达式的结果以及const int 整型变量。
-
形参只能是整型,枚举,指针和引用,如double,string等是不可以的,但是可以使用double&或者string*等类型是允许的。
-
当一个模板被实例化时,非类型参数被一个用户提供的或者编译器推断出的值所代替。正因为模板在编译阶段编译器为我们生成一个对应的版本,所以其值应该能够编译时确定,那么他应该是一个常量或者常量表达式。
-
非类型模板形参的形参和实参间所允许的转换
- 允许从数组到指针,从函数到指针的转换。如:template <int *a> class A{}; int b[1]; A<b> m;即数组到指针的转换。
- const修饰符的转换。如:template<const int *a> class A{}; int b; A<&b> m; 即从int *到const int *的转换。
- 提升转换。如:template<int a> class A{}; const short b=2; A<b> m; 即从short到int的提升转换。
- 整值转换。如:template<unsigned int a> class A{}; A<3> m; 即从int 到unsigned int的转换。
- 常规转换。略。
1 |
|
¶(二)默认模板类型形参:
在c++11新规则中,可以为类模板或者函数模版的类型形参提供默认值。
形式为:template<class T1, class T2, ... , class Tk=默认类型 , ... , class Tn=默认类型 > class 类名{ 类体 };
or template<class T1 =默认类型 , class T2, ... , class Tk=默认类型 , ... , class Tn> 返回类型 类型名(形参列表){函数体}
类模版为多个默认模版参数声明指定默认值时,必须遵照“从右往左”的规则进行指定。
函数模版不需要遵循此规则,可以任意指定默认值。
函数模板的参数推导规则简单来说就是:能够从函数实参中推导出类型的话,那么默认模版参数就不会被使用,否则使用默认模版参数。
因此,函数模版如果需要提供默认类型,因为其形参列表是从右往左给出默认值的,所以只有其默认类型也是从右往左给出且与值的类型匹配(也就是不会出现不匹配情况)、值的个数匹配,才是有效的默认类型。
1 |
|
¶(三)模版的特化:
模版的特化是模版参数在某种特定类型下的具体实现,是对单一模版提供的一个特殊实例,它将一个或多个模版参数绑定到特定的类型或值上。
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,比如字符串不能进行比较,此时就需要特化出一个专门用于字符串的模版参数。
特化分为函数模版特化和类模版特化:
-
函数模板特化:
必须要先有一个基础的函数模板,且使用特换模板函数时格式有要求:
-
关键字template后面接一对空的尖括号<>。
-
函数名<特化类型>(特化类型 参数1, 特化类型 参数2 , …) 在函数名后跟<>其中写要特化的类型。
函数形参表必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
(实际上,此为全特化,c++11中,函数模版也可以进行偏特化的部分特化相似操作,但实际上这是一种重载,会导致函数有多个匹配,此时无法区分;不能进行偏特化的部分限制)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#include <iostream>
using namespace std;
template< typename T >
int compare(const T& a, const T& b)
{
if (a > b)return 1;
else if (a == b)return 0;
else return -1;
}
template<>
int compare(const char* const & a, const char* const & b)
{
return strcmp(a, b);
}
int main()
{
cout << compare(3, 4) << endl;
cout << compare(4.2, 2.3) << endl;
cout << compare((const char*)"vvv", (const char*)"ttt") << endl;
return 0;
} -
-
类模版特化:
分为全特化和偏特化:
-
全特化:将模版参数类型中所有的参数都确定化。
全特化的优先级比偏特化要高。
-
偏特化:对于模版的类型进行限制。
偏特化有两种表现方式:
-
部分特化:将模版参数表中的一部分参数特化。
-
对于模版类型进行进一步的限制。(如必须特化为指针类型、引用类型)
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82#include<iostream>
using namespace std;
//类模版
template<class T1, class T2>
class Data
{
public:
Data() {
cout << "Data<T1,T2>" << endl;
}
private:
T1 _d1;
T2 _d2;
};
//全特化:
template<>
class Data<int, char>
{
public:
Data() {
cout << "Data<int,char>" << endl;
}
private:
int _d1;
char _d2;
};
//偏特化1:部分特化
template<class T1>
class Data<int, T1>
{
public:
Data() {
cout << "Data<int,T1>" << endl;
}
private:
int _d1;
T1 _d2;
};
//偏特化2:添加限制
//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
Data() {
cout << "Data<T1*,T2*>" << endl;
}
private:
T1 _d1;
T2 _d2;
};
//两个参数偏特化为引用类型
template<class T1, class T2>
class Data<T1&, T2&> {
T1& _d1;
T2& _d2;
public:
Data(T1& a, T2& b) :
_d1(a),
_d2(b)
{
cout << "Data<T1&,T2&>" << endl;
}
};
int main()
{
Data<int, int> d1; // Data<T1,T2>
Data<string, string> d2; // Data<T1,T2>
Data<int, char> d3; // Data<int,char>,其实部分特化也满足,但优先进行全特化
Data<int, double> d4; // Data<int,T1>
Data<int*, double*> d5; // Data<T1*,T2*>
int i = 5;
int& pi = i;
double d = 6.5;
double& pd = d;
Data<int&, double&> d6(pi,pd); //Data<T1&,T2&>
return 0;
} -