<四>模板的完全特例化和非完全特例化

模板作为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 = &sum;
	(*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

        }

}
posted @ 2022-12-06 17:26  Hello_Bugs  阅读(203)  评论(0编辑  收藏  举报