标签:
模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。
模板是一种对类型进行参数化的工具,通常有两种形式:函数模板和类模板
(1)、函数模板针对仅参数类型不同的函数;
(2)、类模板针对仅数据成员和成员函数类型不同的类。
使用模板的目的就是能够让程序员编写与类型无关的代码。注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
一、函数模板通式
template <class 形参名,class 形参名,......> 返回类型 函数名(参数列表){ 函数体 }
其中template和class是关键字,class可以用typename 关见字代替,在这里typename 和class没区别,
<>括号中的参数叫模板形参,模板形参和函数形参很相像,模板形参不能为空。一但声明了模板函数就可以用模板函数的形参名声明类中的成员变量和成员函数,即可以在该函数中使用内置类型的地方都可以使用模板形参名。
模板形参需要调用该模板函数时提供的模板实参来初始化模板形参,一旦编译器确定了实际的模板实参类型就称他实例化了函数模板的一个实例。
(模板特化,模板特化必须有模板的前向声明和定义,即标准的模板定义,才能进行!,见5。
标准模板定义中,类名后面不能带任何的模板形参。 )
二、类模板通式
template<class 形参名,class 形参名,…> class 类名{ ... };
template<class T> class A{ public: T a; T b; T hy(T c, T &d); };
在类A中声明了两个类型为T的成员变量a和b,还声明了一个返回类型为T带两个参数类型为T的函数hy。
template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表){ 函数体 }
比如有两个模板形参T1,T2的类A中含有一个void h()函数,则定义该函数的语法为:
template<class T1, class T2> void A<T1,T2>::h(){ }
注意:当在类外面定义类的成员时template后面的模板形参应与要定义的类的模板形参一致。
三、模板的形参
有三种类型的模板形参:类型形参,非类型形参。
类型模板形参:类型形参由关见字class或typename后接说明符构成,如
template<class T> void h(T a){ };
其中T就是一个类型形参,类型形参的名字由用户自已确定。模板形参表示的是一个未知的类型。模板类型形参可作为类型说明符用在模板中的任何地方,与内置类型说明符或类类型说明符的使用方式完全相同,即可以用于指定返回类型,变量声明等。
1、允许从数组到指针,从函数到指针的转换。如:template <int *a> class A{}; int b[1]; 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、常规转换。
template<class T1, class T2=int> class A{ public: void h(); };
定义方法为
template<class T1,class T2> void A<T1,T2>::h(){ }
C++中的class也可以类型参数化,其中容器类是极具这一特征的。对于模板类的基本定义和使用,可以参考STL。
这里可以先将类模板特化与面向对象中的多态进行一个简单的比较,这样可以便于我们对它的理解,也同样有助于指导我们在实际的开发中应用这一C++技巧。
众所周知,对于多态而言,提供的是统一的接口和不同的实现类实例,其最终的行为将取决于实现类中的实现,相信每一个有面向对象基础的开发者对于这一概念并不陌生。
而模板特化则要求必须提供一个标准的模板类(等同于多态中的接口),与此同时再为不同的类型提供不同的特化版本。
在模板特化类中,我们首先需要保证类名是相同的,只是模板参数不再使用抽象的类型,而是直接使用具体的类型来实例化该模板类。
在调用时,编译器会根据调用时的类型参数自动选择最为合适的模板类,即如果有特化模板类中的类型参数和当前调用类型相匹配的话,则首选该特化模板类,否则选择最初的标准模板类
#include <stdio.h> #include <string.h> //1. 这里我们先声明了一个通用类型的模板类。这里要有类型参数必须包含hashCode()方法。 //否则,该类型在编译期实例化时将会导致编译失败。 template <typename T> class CalcHashClass { //该类为标准模板类(等同于多态中的接口) public: CalcHashClass(T const& v) : _value(v) { } int hashCode() { printf("This is ‘template <typename T> class CalcHashClass‘.\n"); return _value.hashCode() + 10000; } private: T _value; }; //2. int类型实例化特化模板类。 template <> class CalcHashClass<int> { public: CalcHashClass(int const& v) : _value(v) { } int hashCode() { printf("This is ‘template <> class CalcHashClass<int>‘.\n"); return _value * 101; } private: int _value; }; //3. const char*类型实例化的特化模板类 template<> class CalcHashClass<const char*> { public: CalcHashClass(const char* v) { _v = new char[strlen(v) + 1]; strcpy(_v,v); } ~CalcHashClass() { delete [] _v; } int hashCode() { printf("This is ‘template <> class CalcHashClass<const char*>‘.\n"); int len = strlen(_v); int code = 0; for (int i = 0; i < len; ++i) code += (int)_v[i]; return code; } private: char* _v; }; //4. 辅助函数,用于帮助调用者通过函数的参数类型自动进行类型推演,以让编译器决定该 //实例化哪个模板类。这样就可以使调用者不必在显示指定模板类的类型了。这一点和多态有 //点儿类似。 template<typename T> inline int CalcHashCode(T v) { CalcHashClass<T> t(v); return t.hashCode(); } //5. 给出一个范例类,该类必须包含hashCode方法,否则将造成编译错误。 class TestClass { public: TestClass(const char* v) { _v = new char[strlen(v) + 1]; strcpy(_v,v); } ~TestClass() { delete [] _v; } public: int hashCode() { int len = strlen(_v); int code = 0; for (int i = 0; i < len; ++i) code += (int)_v[i]; return code; } private: char* _v; }; int main() { TestClass tc("Hello"); CalcHashClass<TestClass> t1(tc); printf("The hashcode is %d.\n",t1.hashCode()); //这里由于为模板类TestClass提供了基于int类型的模板特化类,因此编译器会自动选择 //更为特化的模板类作为t2的目标类。 CalcHashClass<int> t2(10); printf("The hashcode is %d.\n",t2.hashCode()); //在上面的示例中,我们通过显示的给出类型信息以实例化不同的模板类,这是因为模板类 //的类型信息是无法像模板函数那样可以通过函数参数进行推演的,为了弥补这一缺失,我们可以 //通过一个额外的模板函数来帮助我们完成这一功能。事实上,这一技巧在Thinking in Java中 //也同样给出了。 printf("Ths hashcode is %d.\n",CalcHashCode(10)); printf("Ths hashcode is %d.\n",CalcHashCode("Hello")); return 0; } //This is ‘template <typename T> class CalcHashClass‘. //The hashcode is 10500. //This is ‘template <> class CalcHashClass<int>‘. //The hashcode is 1010. //This is ‘template <> class CalcHashClass<int>‘. //Ths hashcode is 1010. //This is ‘template <> class CalcHashClass<const char*>‘. //Ths hashcode is 500.
通过上面的示例可以看出,模板特化是依赖于编译器在编译期动态决定该使用哪个特化类,或是标准模板类的。相比于多态的后期动态绑定,该方式的运行效率更高,同时灵活性也没有被更多的牺牲。
有的书中将其翻译成模板偏特化,或者是模板的局部特化,但含义都是相同的。为了便于理解,我们可以将上面的模板特化称为模板全部特化,即模板类的类型参数全部被特化了。顾名思义,模板部分特化只是将其中一部分类型参数进行了特化声明,因此也可以将模板特化视为模板部分特化的一种特殊形式。由于应用场景基本相同,因此下面的代码将仅仅给出最基本的示例和注释说明,以帮助大家熟悉他的语法即可:
//1. 标准模板类。 template<typename T1, typename T2> class MyClass { ... ... }; //2. 两个模板参数具有相同类型的部分特化类。 template<typename T> class MyClass<T,T> { ... ... } //3. 第二个类型参数是int template<typename T> class MyClass<T,int> { ... ... } //4. 两个模板参数都是指针。 template<typename T1,typename T2> class MyClass<T1*,T2*> { ... ... } //5. 两个模板参数都是相同类型的指针。 template<typename T> class MyClass<T*,T*> { ... ... } //6. 调用示例代码。 int main() { MyClass<int,float> c1; //调用MyClass<T1,T2> MyClass<float,float> c2; //调用MyClass<T,T> MyClass<float,int> c3; //调用MyClass<T,int> MyClass<int*,float*> c4; //调用MyClass<T1*,T2*> MyClass<int*,int*> c5; //调用MyClass<T*,T*> return 0; }
在此之前,我们了解到的有关该关键字的用途更多是针对模板参数的定义。而这里介绍的使用方式则有些不同,主要区别是
typename的这种使用方式用于定义或提示编译器,其后的修饰标识符是模板参数中的类型标识符,而不是普通的静态类成员。见以下代码示例和关键性注释。
#include <stdio.h> template<typename T> class MyTestClass { public: //这里的typename是用于通知编译器,其后的MyType是模板T定义的内部类型,从这个代码示例 //中可以看出,不管是在函数参数、返回值还是定义成员变量定义,都要遵守这一语法规则。 MyTestClass(typename T::MyType p) : _p(p) { } typename T::MyType GetData() const { return _p; } public: typename T::MyType _p; }; //在该类中,由于后面的调用需要将其用作MyTestClass的模参,因此他必须typedef名字为MyType的 //内部类型,否则将会导致编译失败。 class TemplateParamClass { public: typedef const char* MyType; }; int main() { MyTestClass<TemplateParamClass> t("Hello"); printf("The data is %s.\n",t.GetData()); return 0; }
[1]. Step By Step(C++模板类) : http://www.cnblogs.com/stephen-liu74/archive/2012/08/22/2599400.html
标签:
原文地址:http://www.cnblogs.com/yaozhongxiao/p/4391307.html