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