为了能更方便的使用STL模板库,先行了解模板与泛型编程的语法和思想
模板就是泛型编程,为了编写更加通用的程序代码,实现算法与类分离————>代码复用
也即编写更加抽象的类和函数。
函数模板template函数模板实例化—>模板函数返回值类型 函数名(参数列表){ 函数体; } //template是模板关键字<模板类型参数> //typename可以用class替换; //模板类型参数可以有很多,不是具体的类型 //每一个模板参数都需要用", "隔开
- 实例化后变为模板函数:函数模板需要用具体的类型实例化
- 使用:函数模板名<类型参数列表> (实参列表)
-
某些支持自动类型推导
-
使用时传递了实参,根据实参类型进行推导,
但有些情况可能产生二义性,此时需要提供<类型参数>
-
-
某些不支持
template
void func(){//模板没有参数列表 cout << typeid(T).name() << endl; cout << CAP << endl; } int main(int argc,char *argv[]){ func();//没有实例化类型 return 0; } - 模板没有参数列表
- 多种类型
- 没有实例化类型
针对某些特殊的类型(C风格的字符串),通用的模板可能不适用
需要针对该类型写一个特化的函数(但此前须有通用版本才能称为特化)
template<> 返回值类型 函数名(参数列表){ 函数体; }函数模板支持非类型参数
//c++11 templateint compare(const char (&p1)[N],const char (&p2)[M]){ cout << N << "," << M << endl; return strcmp(p1,p2); } int main(int argc,char *argv[]){ int ret = compare("Hello","hi"); cout << ret << endl; int a = 6,b = 3; compare<6,3>("Hello","hi"); //compare("Hello","hi"); const int x = 6, y = 3; compare ("Hello","hi"); char s1[] = "Hello"; char s2[] = "hi"; const int m = strlen(s1)+1,n = strlen(s2)+1; //compare ("Hello","hi"); return 0; }
在对非类型模板参数实例化时,只能用常量或者常量表达式
模板编译当编译器遇到一个模板定义时, 它并不生成代码,只有当实例化出模板的一个特定版本时, 编译器才会生成代码,所以模板会二次编译,一次编译检查模板本身的语法, 第二次是模板实例化后,生成特定的版本和类进行编译
在多文件编程中,函数和类的声名放在头文件中,函数和类的实现放在文件中.cpp
但是模板的声名和定义是不能放在不同的文件中,因为如果只包含声名,在实例化时,将无法得到模板的定义,所以模板编程中会把模板的声名和定义直接放在头文件中
编译错误报告-
编译模板代码,针对模板本身进行语法检查,需要代码满足模板规则
-
使用模板实例时,检查实例化时模板参数的个数是否相等 ,类型是否匹配
-
模板实例化,根据模板用具体的实例化,生成具体的模板函数或者模板类
- 再次编译模板函数和模板类,进行语法检查,之哦于在这个阶段才能发现类类型的问题(如:不支持的类型,没有连接到函数)
如果普通函数和模板函数除了函数体外完全一致时, 会优先调用普通函数,如果像调用函数模板,则需要显示类型实例化,函数模板中针对类型特化的版本,则调用特化的版本。
template尾置返回类型void func(T a){} tmeplate<> void func(int a){} void func(int a){} func(10);//普通 func (10);//特化 func('a');//通用
template类模板auto maxarr(T beg, T end)->decltype(*beg){ }
#include#include using namespace std; //通用的类模板 template class Compare{ public: Compare(const T& a,const T& b):a(a),b(b){ cout << "通用版本" << endl; } T max(void){ return a return a return a; } T getSecond(){ return b; } private: T a; T b; }; //成员特化 template<> char* Compare ::max(){ cout << "成员特化" << endl; return strcmp(a,b)<0?b:a; } template<> char *Compare ::min(){ cout << "成员特化" << endl; return strcmp(a,b)<0?a:b; } //全员特化版本 template<> class Compare { public: Compare(const char * const & a,const char * const & b):a(a),b(b){ cout << "特化" << endl; } const char *max(void){ return strcmp(a,b)<0?b:a; } const char *min(void){ return strcmp(a,b)<0?a:b; } const char *getFirst(){ return a; } const char *getSecond(){ return b; } private: const char *a; const char *b; }; int main(int argc,char *argv[]){ Compare c(1,3);//无法进行隐式类型推导 cout << c.max() << endl; cout << c.min() << endl; Compare c1("Hello","hello"); cout << c1.max() << endl; cout << c1.min() << endl; char s1[10] = "hello"; char s2[10] = "Hello"; Compare c2(s1,s2); cout << c2.max() << endl; cout << c2.min() << endl; return 0; }
- 类模板无法进行自动类型推导
-
类所有的属性和方法重写
template<> class 类模板名<特化的类型>{ }
-
只针对几个函数特化
template<>返回值类型 类模板名<特化的类型>::成员方法名(参数列表){ }
#includeusing namespace std; template class C{ public: C(){ cout << "T" << endl; } }; //针对数组的偏特化 template class C { public: C(){ cout << "T[]" << endl; } }; //针对指针的偏特化 template class C { public: C(){ cout << "T*" << endl; } }; template class A{ public: A(){ cout << "T1,T2,T3" << endl; } }; template class A { public: A(){ cout << "T1,T2,T2" << endl; } }; template class A { public: A(){ cout << "T1,T1,T1" << endl; } }; template class A { public: A(){ cout << "T1,T2[],T2[]" << endl; } }; template class A {//针对指针的偏特化 public: A(){ cout << "T1*,T1*,T1*" << endl; } }; template class A {//针对三个类型相同情况下的偏特化 public: A(){ cout << "T1[],T1[],T1[]" << endl; } }; int main(int argc,char *argv[]){ C c1; C c2; C c3; A a1; A a2; A a3; A a4; A a5; A a6; return 0; }
- 针对指针或者数组类型的偏特化,或者针对类型本身特殊情况的特化
- 在实例化时会选择特化程度更高的实例化
非类型参数在实例化时必须是常量或者常量表达式
类模板允许缺省值缺省模板参数需要满足靠右原则
函数模板在C++11以后也允许有模板缺省参数
普通类中含有模板成员方法 可变长类型模板参数void func(){ } templatevoid func(const T& t,const Args& ...args){ //cout << t << " " << endl; func(args...);//func() } template class X{ template void emplace(iterator pos,Args&& ...args){ } };