c++类模板深度剖析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++类模板深度剖析相关的知识,希望对你有一定的参考价值。

1、类模板的泛指类型可以定义多个

template <typename T1, typename T2>
class Test
{
public:	
	void add(T1 a, T2, b);
};

使用时: Test<int, float> t1;	//T1的泛指类型就是int, T2的泛指类型就是float。

2、类模板的特化类型

(1)意思就是如果定义了一个类模板,这个类模板的参数有多个,但是如果当使用这个类模板的时候,我们传递参数时,参数的类型是一样的话,编译器就会将类模板特化成一个参数的类模板。

(2)类模板的特化分为:部分特化,和完全特化。

部分特化:用特定规则约束类型参数,部分类型参数必须显示指定,根据类型参数分开实现类模板
如:

template										特化后				template	
< typename T1, typname T2 >											< typename T >		
class Test															class Test <T, T>	//部分特化的约束条件
{																	{
			
};																	};
当类型参数不同的时候,会选择用左边的那个类模板,当类型参数相同的时候。class Test< T, T>就是约束条件


完全特化:完全显示指定类型参数
如:
template										特化后				template	
< typename T1, typname T2 >											<  >	//完全特化时,不需要声明泛指类型
class Test															class Test <int, int> //这个就是完全特化
{																	{
			
};																	};
当使用类模板的时候,并且指定了所有的类型参数全都是一样的,比如int的时候,编译器就会将左边的类模板特化成右边的类模板,此时该类模板中就没有泛指类型了。
当类型参数不同的时候,就会使用左边的类模板。

(3)根据实验的例子,类模板的特化就是程序中有了一个左边的类模板的时候(这个类模板的有两个泛指类型),但是同时程序中我们又定义了一个同名的类模板,但是这个类模板的两个泛指类型都是一样的。这个时候,当我们在程序中使用这个类模板的时候,如果参数类型是不一样的,编译器就会使用左边的那个,如果参数类型是一样的时候,编译器就会使用右面的那个。编译器会认为右边的那个类模板,是左边的类模板的一种特化,编译器不会认为左边的类模板是一个新的类模板,只是一个左边类模板的一个特化,所以编译能够通过。

总结:将一个类模板,根据不同的类型参数情况进行分开实现这个类模板,其实就是一个类模板的特化过程。

例:

#include <iostream>

using namespace std;


template 
< typename T1, typename T2 >
class Test
{
public:
	void add(T1 a, T2 b)
	{
		cout << "void add(T1 a, T2 b)" << endl;
		cout << a + b << endl;
	}
};


/*****************************************************部分特化*************************************************/


template
< typename T>
class Test < T, T >		//这个地方加上了约束条件,虽然这个类模板跟上面的那个类模板同名,编译器不会认为这个类模板是一个新的类模板,而是上面类模板的一个特化形式,
{				//当我们使用Test类模板的时候,如果指定的参数类型不同的话,编译器就会使用上面的那个类模板实现,如果指定参数类型相同的话,编译器就会使用这个类模板的实现
public:
	void add(T a, T b)
	{
		cout << "void add(T a, T b)" << endl;
		cout << a + b << endl;
	}
	void print(void)		//即使多出来了个print成员函数,编译也是通过的,所以是支持这种特化方式的
	{
		cout << "class Test < T, T >	" << endl;
	}
};

/******************************************************完全特化**********************************************************/

template
< >		//进行类模板的完全特化时,不用进行泛指类型的声明
class Test <void *, void *>		//当使用Test类模板时,参数类型都为void *时,就会使用这个实现。
{
public:
	void add(void * a, void * b)
	{
		cout << "void add(void * a, void * b)" << endl;
		cout << "Error run not to add because type is void *..." << endl;
	}
};

/*****************************************将Test类模板特化出一个两个参数分别是指针的情况**********************************************/

template 
< typename T1, typename T2>
class Test < T1 *, T2 *>
{
public:
	void add(T1 *a, T2 *b)
	{
		cout << "void add(T1 *a, T2 *b)" << endl;
		cout << *a + *b << endl;
	}	
};

int main(void)
{
	int a = 1;
	double b = 1.0;
	
	Test<int, float> t1;	//使用的就是没有特化的Test类模板
	Test<long, long> t2;	//使用的就是特化后的Test类模板
	Test<void *, void *> t3;
	Test<int *, double *> t4;
	
	t1.add(2, 2.5);
	t2.add(10, 10);
	
	t2.print();
	
	t3.add(&a, &b);
	
	t4.add(&a, &b);
	
	return 0;
}



3、继续理解类模板的特化

(1)类模板的特化的就是根据需要,将一个类模板进行分开来实现。特化只是模板的分开实现,本质上还是同一个类模板。特化类模板的使用方式是统一的,就是必须显示的指定每一个类型参数。


(2)问题:类模板特化与重定义有区别吗?
答:有区别,重定义和特化不同。在本质上,如果将一个类模板进行重定义,那么要么就是实现了一个新的类,要么就是最后会出现两个类模板。本质上不同。使用的时候不能进行统一使用,
在使用时我们要考虑选择哪个。

特化的本质是,只实现同一个类模板,只不过这个类模板是分开来实现的,这就是本质上的不同。在使用时是用统一的方式进行使用类模板和特化类,因为本质上都是实现了一个类模板,使用时
编译器会根据不同的参数类型来选择去使用那个类模板还是特化类。所以类模板的特化就是将一个类模板进行分开来实现,这句话是非常重要的。

(3)问题:函数模板可以特化吗?
答:函数模板只支持类型参数的完全特化,不支持部分特化,也就是在函数名的后面显示的指定出具体的参数类型。

如:函数模板的完全特化

template
< typename T >
bool Equal(T a, T b)	//函数模板的定义
{
	return a == b;
}

template	
< >						
bool Equal<void *>(void *, void *)	//函数模板的完全特化
{
	return a == b;		
}

(4)工程中的建议:当需要重载函数模板的时候,我们要优先考虑使用模板特化的方式去分开实现一个函数模板,而是不用重载函数模板的方式去新的实现了一个函数,当模板特化无法满足要
求的时候,在使用函数重载。

工程中使用模板特化来代替类(函数)重定义。


例:类模板的部分特化、完全特化,函数模板的完全特化。优先考虑使用特化的方式当需要将模板进行重定义添加功能时。

#include <iostream>
#include <string>

using namespace std;


/*
*	特化的方式实现一个类模板,本质就是分开实现类模板
*
*/
template
<typename T1, typename T2> 
class Why
{
public:
	void print(T1 a, T2 b)
	{
		cout << "void print(T1 a, T2 b)" << endl;
		cout << a << " " << b << endl;
	}
};

template
<typename T>
class Why<T, T>	//为类模板的部分特化
{
public:
	void print(T a, T b)
	{
		cout << "void print(T a, T b)" << endl;
		cout << a << " " << b << endl;
	}	
};

template
< >
class Why<int, int>		//为类模板的完全特化
{
public:
		void print(int a, int b)
		{
			cout << "void print(int a, int b)" << endl;
			cout << a << " " << b << endl;
		}
};

/*
*	函数模板的特化只支持完全特化
*	实现一个函数模板,并且完全特化,也就是分开实现一个函数模板,这样叫做特化。
*/
template 
<typename T>
bool Equal(T a, T b)
{
	return a == b;
}

template
< >		//函数模板的完全特化,之所以要特化这个函数模板是因为,浮点数的比较不单纯的只用==来比较。
bool Equal<double>(double a, double b)
{
	const double delta = 0.00000000001;
	double r = a - b;
	
	cout << "bool Equal<double>(double a, double b)" << endl;
	
	return ((-delta < r) && (r < delta)); 
}

/*
*	重载Equal函数的方式来达到比较浮点数的方法,但这种方式是不优先考虑的,因为这种方式,在使用的时候要考虑如何选择,要优先考虑使用特化的方式。
*	能使用特化的方式时,就不考虑使用这种函数重载的方式,不论对于类模板还是函数模板都是这样的,优先考虑使用特化的方式来分开实现,因为这样都是为了实现一个类模板或函数模板。
*/
bool Equal(double a, double b)
{
	const double delta = 0.00000000001;
	double r = a - b;
	
	cout << "bool Equal(double a, double b)" << endl;
	
	return ((-delta < r) && (r < delta));
}


int main(void)
{
	double a = 0.0, b = 0.0;
	
	Why<int, double> w1;
	Why<string, string> w2;
	Why<int, int>w3;
	
	w2.print("why", "fangqingqing");
	w1.print(1, 2.2);
	w3.print(100,100);
	
	cout << "Please input two double number..." << endl;
	
	cin >> a >> b;
	
	cout << Equal<double>(a, b) << endl;
	
	cin >> a >> b;
	
	cout << Equal(a, b) << endl;
	
	
	return 0;
}


以上是关于c++类模板深度剖析的主要内容,如果未能解决你的问题,请参考以下文章

类模板深度剖析

第59课 类模板深度剖析

C++深度剖析学习总结 21 友元的的概念

C++初阶:vector类vector的介绍及使用 | 迭代器失效问题 | vector的深度剖析及模拟实现

C++深度剖析学习总结 22 类中的函数重载

C++深度剖析学习总结 22 类中的函数重载