C++的“模板”
前言:
为了实现泛型编程,增强代码的灵活性,对此C++通过模板来实现,在工程中熟练掌握模板的使用,是非常重要的基本功,本文对C++的模板进行了详细地讲述,方便我们学习并且熟练掌握这个语法
模板的使用:
模板的格式:在函数头或者类头部加上这个一句即可,可以用 typename或者class是用来定义模板参数关键字
template<typename T>
template<class T>
template<class T1,class T2,...>
函数模板和类模板:
函数模板:
函数模板本质是一个蓝图(模具),它不是具体的函数,根据实例化的类型,编译器才会自动生成对应的函数
实例化:
隐式实例化:让编译器根据实参推演模板参数的实际类型
如下图所示:
template<typename T>
T Add(T val1,T val2)
{return val1+val2;
}
int main()
{int a=1,b=2;Add(a,b);//通过a,b实参的int类型,推导出T为整形的Add函数double c=1.1;Add(a,c);//会有问题,函数模板中一个T推导式int,一个T推导是double,导致混乱进而报错//可以通过类型强转来实现Add((double)a,c);return 0;
}
隐式实例化无法进行隐式类型转换,这个时候除了像上述代码强转以外,还可以进行显示实例化
显示实例化: 在函数名后的<>中指定模板参数的实际类型
template<typename T>
T Add(T val1,T val2)
{return val1+val2;
}
int main()
{int a=1,b=2;Add(a,b);//通过a,b实参的int类型,推导出T为整形的Swap函数double c=1.1;//Swap(a,c);//会有问题,函数模板中一个T推导式int,一个T推导是double,导致混乱进而报错//可以通过类型强转来实现Add((double)a,c);return 0;
}
如果类型不匹配的话,会进行隐式类型转换,如果转换失败编译器则会报错
模板函数匹配规则:
1.针对存在模板函数和非模板函数同时存在,如果模板函数隐式实例化后等于非模板函数则会先调用非模板函数。如果不等于的话,则会调用更匹配的模板函数。
template<typename T>
T Add(T val1,T val2)
{cout<<"Add(T ,T)"<<endl;return val1+val2;
}
int Add(int a,int b)
{cout<<"Add(int ,int)"<<endl;return a+b;
}
int main()
{int a=1,b=2;double c=1.1,d=2.2;Add(a,b);Add(c,d);Add<int>(a,b);return 0;
}
运行结果:
2. 模板函数隐式实例化不允许类型转换,但普通函数可以进行类型转换
类模板:
类模板本质跟模板函数一样是一个蓝图(模具),它不是具体的类,根据实例化的类型,编译器才会自动生成对应的类
实例化:
类的实例化一般都是隐式实例化。
隐式实例化:类名<类型>对象名;
如下图的Test<int> t;
template<class T>
class Test
{public:Test(T a):_a(a){cout<<"Test: a"<<_a<<endl;}private:T _a;
};
int main()
{Test<int> t(10);return 0;
}
非模板类型参数:
- 类型形参作为类型传递
- 非类型形参作为常量使用
使用:
template<class T,size_t N=10>
class MyArry
{public:MyArry():_nums(N){cout<<"nums的size大小"<<N<<endl;}private:vector<T>_nums;
};
int main()
{MyArry<int>arr1;MyArry<int,20>arr2;return 0;
}
这里的N就是非类型形参,作为常量使用
注意:
- 1.浮点数、类对象以及字符串是不允许作为非类型模板参数的。
- 2. 非类型的模板参数必须在编译期就能确认结果。
模板的特化:
函数模板特化:
使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,对于这些特殊类型我们要进行特殊处理,如下文所示:
template<class T>
bool Compare(T val1,const T val2)
{return val1>val2;
}
int main()
{int a=10,b=5;int*p1= &a;int *p2=&b;cout<<Compare(a,b)<<endl;cout<<Compare(p1,p2)<<endl;return 0;
}
运行结果不符合我们预期
那么对于int*这种函数的比较,我们要单独处理,进行函数的特化
template<>
bool Compare<int*>( int* pval1, int* pval2)
{cout<<"int*特化Compare函数"<<endl;return *pval1>*pval2;
}
进行特殊处理胡,运行结果符合预期:
对于函数来讲,一般情况下如果函数模板遇到不能处理或者处理有误的类型,一般都会单独写一个函数来解决这个问题,简单明了,可读性高,函数的特化由于受限模板参数的影响可能更繁琐
类模板特化:
同样的针对特殊类型的类,我们要特化进行特殊处理,而类的特化分为全特化和偏特化
全特化: 全特化即是将模板参数列表中所有的参数都确定化
template<class T1,class T2>
class Example
{public:Example(){cout<<"Example<class T1,class T2>"<<endl;}
};
template<>
class Example<int,char>
{public:Example(){cout<<"Example<int,char>"<<endl;}
};
int main()
{Example<int,double>e1;Example<int,char>e2;return 0;
}
运行结果符合预期:
偏特化:偏特化即是将模板参数列表中部分的参数都确定化。
template<class T1,class T2>
class Example
{public:Example(){cout<<"Example<class T1,class T2>"<<endl;}
};
template<>
class Example<int,char>
{public:Example(){cout<<"Example<int,char>"<<endl;}
};
template<class T1>
class Example<T1,int>
{public:Example(){cout<<"Example<T1,int>"<<endl;}
};
template<class T1>
class Example<T1*,int*>
{public:Example(){cout<<"Example<T1*,int*>"<<endl;}
};
int main()
{Example<int,double>e1;Example<int,char>e2;Example<char,int>e3;Example<int*,int*>e4;return 0;
}
通过偏特化,限制第二个类型形参,对于int和int*类型的都会进行特殊处理
在工程中,偏特化可以起到限制参数的作用
总结:
模板的出现让C++可以实现泛型编程,通过模板复用代码,节省资源,增强了代码的灵活性。但是模板也有缺点,如果有多个不同类型的实例化,编译器会生成很多函数代码,导致代码膨胀,使得编译器时间变长,并且如果出现了模板编译错误,不容易定位错误。
还有一个简单的思维导图,方便大家复习巩固:
结语:
以上就是我分享的C++模板的全部内容了,希望对大家有些帮助,也希望与一样喜欢编程的朋友们共进步
谢谢观看
如果觉得还阔以的话,三连一下,以后会持续更新的,我会加油的
祝大家早安午安晚安