<四>模板的完全特例化和非完全特例化
模板作为C++泛型编程的基础十分重要,其使得一份代码能用于处理多种数据类型。而有些时候,我们会希望对一些特定的数据类型执行不同的代码,这时就需要使用模板特例化(template specialization)。
函数模板特例化
首先函数模板的特例化并不是函数重载,每一个特例化实际上是提供了另一个模板定义式,因此特例化不影响函数匹配。
特例化一个函数模板时,必须为模板中的每个参数都给出实参,关键字template后跟一个<>
1 #include<iostream>
2 #include<typeinfo>
3 #include<string.h>
4 using namespace std;
5
6 template<typename T>
7 bool com(T a,T b)
8 {
9 cout<<"type(T): "<<typeid(T).name()<<endl;
10 cout<<"type(a): "<<typeid(a).name()<<endl;
11 cout<<"type(b): "<<typeid(b).name()<<endl;
12 return a > b;
13 }
14
15 template<>
16 bool com<const int>(const int a,const int b)
17 {
18 cout<<"template<>:const int"<<endl;
19 return a>b;
20 }
21
22 template<>
23 bool com<const char *>(const char*a,const char*b)
24 {
25 cout<<"template<>:const char*"<<endl;
26 return strcmp(a,b) > 0 ? true:false;
27 }
28 int main()
29 {
30 int a =10;
31 int b =11;
32 com(a,b);
33 return 0;
34 }
对于const int的特例化函数对应的函数名字的字符串大致为: " com_const_int"
但是在实例化点 它想要的函数名是个"com_int"
这样一比对,名字对不上号,所以就不会调用该类型的特例化版本,就会自己去生成一份代码
(尽管说int和const int对于编译器来说类型是相同的,但是组成函数名还是有影响的,'
尽管类型看似一样,生成的函数名不同,那么期望的函数与实际存在的特例化函数名就对不上号)
类模板特例化
类模板中,提供所有实参时叫做全特化,类模板的全特化和函数模板的特例化上没有什么不同(除了一个是类模板一个是函数模板:D)
与函数模板必须为每个模板参数提供实参不同,类模板支持部分特例化,也叫偏特化(partial specialization)
当定义类模板的偏特化版本时,模板参数只需包含原模板中那些还未确定类型的模板参数,在类名之后需要添加<>,且需要按对应位置填入实参。
template<typename T, typename P>
class test {
public:
test() { cout << "这是未特化的类模板\n"; }
};
template<>//全特化,template<>可以为空
class test<int*, int*> {
public:
test(){ cout << "这是接受两个指针类型的全特化的类模板\n"; }
};
//当定义类模板的偏特化版本时,模板参数只需包含原模板中那些还未确定类型的模板参数,在类名之后需要添加<>,且需要按对应位置填入实参
template<typename P>//T被特化为bool类型,template<>中无需typename T
class test<bool,P> {
public:
test() { cout << "这是bool偏特化的类模板\n"; }
};
int main()
{
test<int*, int> t1;//会调用未特化的类模板
test<bool,int> t2;//会调用bool偏特化
test<int*, int*> t3;//全特化版本
return 1;
}
#include <iostream>
using namespace std;
template<typename T>
class MyVector {
public:
MyVector<T>() { cout << "MyVector<T>()" << endl; }
};
//针对char * 模板完全特例化版本
template<>
class MyVector<char *> {
public:
MyVector() { cout << "MyVector<char *>" << endl; }
};
//模板特例化,只针对指针类型提供的部分特例化,只知道指针,但是什么指针不知道
template<typename Ty>
class MyVector<Ty*> {
public:
MyVector() { cout << "MyVector<Ty *>" << endl; }
};
//接收函数指针,该函数指针指向的函数返回值是int,函数有两个形参int
template<>
class MyVector<int(*)(int, int)> {
public:
MyVector() { cout << "int (*)(int,int )" << endl; }
};
//接收函数指针,该函数指针指向的函数返回值是T,函数有两个形参类型是T 比上面的更通用
template<typename T>
class MyVector<T(*)(T, T)> {
public:
MyVector() { cout << "MyVector<T(*)(T, T)>" << endl; }
};
//模板部分特例化,接收函数指针类型,函数有1个返回值,2个形参,两个形参类型分别是R2,R3 注意和下面区别
template<typename R1, typename R2, typename R3>
class MyVector<R1(*)(R2, R3)> {
public:
MyVector() { cout << "MyVector<R1(*)(R2, R3)>" << endl; }
};
//模板部分特例化,接收函数类型,函数有1个返回值类型是R1,2个形参类型分别是R2,R3,注意和上面区别
template<typename R1, typename R2, typename R3>
class MyVector<R1(R2, R3)> {
public:
MyVector() { cout << "MyVector<R1(R2, R3)>" << endl; }
};
int sum(int x, int y) {
return x + y;
}
int main() {
MyVector<int> v1; //缺省模板
// char * 完全特例化模板
MyVector<char *> v2;/
// 使用部分特例化 class MyVector<Ty*>
MyVector<int *> v3;
//!注意:有完全特例化版本就用完全特例化版本,没有就用部分特例化版本,再没有就使用缺省模板
//使用部分特例化 class MyVector<Ty*> , 可以接收任何指针类型,例如函数指针=>int (*)(int,int), 整形指针,范围太范了.
// 如何针对 MyVector<int (*)(int,int)> v4;写一个完全特例化版本? 写法如下
template<>
class MyVector<int(*)(int, int)> {
public:
MyVector() { cout << "int (*)(int,int )" << endl; }
};
//上面这个模板就写了特例化,专门针对函数指针类型,不接收整形指针,字符指针等其他指针,还可以进一步优化如下
// 如何针对 MyVector<int (*)(int,int)> v4;写一个部分特例化版本? 写法如下两种
// 针对函数指针的部分特例化,带1个返回值,两个参数的函数指针
// 写法1 MyVector元素是函数指针,返回类型是T,两个参数类型也是T, 提供部分特例化
template<typename T>
class MyVector<T(*)(T, T)> {
public:
MyVector() { cout << "MyVector<T(*)(T, T)>" << endl; }
};//这种写法接收函数指针,返回值类型不固定为int了,比上面更灵活了
//写法2 MyVector 元素是函数指针,返回类型是R1,两个参数类型分别是R2,R3 提供部分特例化
template<typename R1, typename R2, typename R3>
class MyVector< R1(*)(R2, R3) >
{
public:
MyVector() { cout << "MyVector<R1(*)(R2, R3)>" << endl; }
};//这种写法比上面好处是,返回值,形参可以分别不一样了,更加灵活
//元素是函数类型,将使用默认的函数模板,也可以加上如下部分特例化模板定义
MyVector<int(int, int)> v5;
template<typename R1, typename R2, typename R3>
//针对函数类型,进行部分特例化,有一个返回值,有两个形参
class MyVector<R1(R2, R3)>
{
public:
MyVector() { cout << "MyVector<R1(R2, R3)>" << endl; }
};
//关于函数类型和函数指针类型 如下
//函数指针类型
typedef int (*PFUNCTION) (int, int);
PFUNCTION pf1 = sum;
pf1(12, 13);
//函数类型
typedef int PFUNCTION2 (int, int);
PFUNCTION2 * pf2 = ∑
(*pf2)(12, 13);
system("pause");
//有完全特例化能匹配就用完全特例化版本,有部分特例化版本就用部分特例化,再没有就用缺省模板
return 0;
}
template<typename T>
void function(T _t) {
cout << typeid(T).name() << endl;
}
//上面这种写法可以,问题:typename T 这种方式范围太大了!!优化如下
template<typename R, typename R1, typename R2>
//细分到可以取出返回值类型R1,形参1类型R2,形参2类型R3 还可以继续细分 如下
void function(R1(*)(R2,R3)) {
cout << typeid(R1).name() << endl;
cout << typeid(R2).name() << endl;
cout << typeid(R3).name() << endl;
}
//效果,继续细分到可以取出返回值类型R1,哪个类类型T,形参1类型R1,形参2类型R2 Good
template<typename R, typename T, typename R1, typename R2>
void function3(R1(T::*)(R2, R3)) {
cout << typeid(T).name() << endl;
cout << typeid(R1).name() << endl;
cout << typeid(R2).name() << endl;
cout << typeid(R3).name() << endl;
}
int add(int a,int b){
return a+b;
}
class Test {
public:
int sum(int x, int y) { return x + y; }
};
int main() {
function(10);//=>模板实参推演,推演出 typeid(T).name() 为 int
function("aaa");//=>模板实参推演,推演出 typeid(T).name() 为 cont char *
function(add);//=>模板实参推演出 typeid(T).name() int (*)(int,int) 即函数指针
如果是下面这样的模板定义
template<typename T>
void function(T * _t) {
cout << typeid(T).name() << endl;
}
那么function(add); 模板实参推演 出来 typeid(T).name() 是 int (int ,int) 函数类型
function3(&Test::sum);将使用下面的模板
template<typename R, typename T, typename R1, typename R2>
void function3(R1(T::*)(R2, R3)) {
cout << typeid(T).name() << endl; //实参推演 T 为 Class Test
cout << typeid(R1).name() << endl;//实参推演 R1 为 int
cout << typeid(R2).name() << endl;//实参推演 R2 为 int
cout << typeid(R3).name() << endl;//实参推演 R3 为 int
}
}