1//函数模板的泛化应用/
函数模板,泛型编程思想,代码复用性强,使用方便。
查看如下代码:
template<class T> //typename
T compare(T x, T y)
{
return x+y;
}
int main()
{
int x=10,y=20;
string s1 = "acf";
string s2 = "ved";
auto res = compare(x,y);
auto res1 = compare(s1,s2);
cout<<res<<endl;
cout<<res1<<endl;
return 0;
}
#endif
输出:
30
acfved
2. /部分特化:编译时确定数组的个数/
#include<cstring>
template<unsigned N, unsigned M>
int compare(const char(&p1)[N],const char(&p2)[M])
{
return strcmp(p1,p2);
}
int main()
{
string a="123";
string c ="bac";
int res = compare("aaha","aaha");
cout<<res<<endl;
return 0;
}
完全特化的一个例子:
template<class T>
void fun(T a){cout<<"普通函数模板"<<endl;}
template<>
void fun<const char *>(const char *a){
cout<<"完全特化"<<a<<endl;
}
int main()
{
int a=10;
const char ar[] = "yhpign";
//fun(a);
fun(&a);
fun<const char *>(ar);此加尖括号显示表示执行完全特化中const char * 类型函数
fun(ar);不加尖括号会随机执行类型兼容的函数体
return 0;
}
输出:
普通函数模板
完全特化yhpign
完全特化yhpign
完全特化注意:1.必须有一个主模板类 2.模板类型被全部明确化
3.函数模板几个灵活使用的例子
/*函数模板的几种灵活使用方式*/
template<class T,class N >
T Max(T x, N y){
cout<<"普通函数模板"<<endl;
return x>y;
}
template<class T>
int Max(int x,T y){
cout<< "部分特化"<<endl;
return x>y;
}
template<class T,class N>
int Max(T* x,N* y){
cout<< "形参以指针方式使用 "<<endl;
return *x>*y;
}
template<class T,class N>
int Max(vector<T>&left,vector<N>&right)
{
cout<< "形参确定了一个参数是模板类类型"<<endl;
return left.size()>right.size();
}
int main()
{
int x =10,y = 20;
//1 ,int res = Max(x,y); //隐式调动,若不显示指定参数类型,则在上述函数模板中可能不会 调用你想调动的函数
int res = Max<int,int>(x,y);//显示调动,确定调动的函数模板
//cout<<res<<endl;
const char *ps = "1";
//2,
int res1 = Max(x,'1');
//3,
int res2 = Max(&x,ps);
vector<int> vecLeft{0};
vector<char> vecRight{'1','2','3'};
//4,
int res3 = Max(vecLeft,vecRight);
return 0;
}
输出:
普通函数模板
部分特化
形参以指针方式使用
形参确定了一个参数是模板类类型
部分特化注意:1.必须有一个主模板类 2.模板类型被部分明确化
例子1:我们简单的实现大小的比较的泛型编程:
class Int{
int val_;
public:
Int(int val):val_(val){}
~Int(){}
operator int()const //没const编译不通过//括号后加const啥用?
//作用1.类成员不可发生改变
//作用2.类是const 类时,类的成员函数后要加const (常性)否则编译不通过。
{
return val_;
}
};
template<class T>
const T Min(const T&x,const T&y) //此时Int类的a是const Int& 类型
{ //,调强转函数,后面肯定要加const才能通过编译
return x > y ? y:x;
}
int main()
{
Int a(3),b(4);
int res = Min(a, b);
cout << res << endl;
return 0;
}
例子2:以上看似没什么问题,但是要让字符串比较,就会出现问题,之前比较函数比较的是地址的大小。我们需要在此基础上写一个部分特化:
template<class T>
const T& Min(const T&x,const T&y)
{
return x>y?y:x;
}
template<>
const char * const & Min<const char *>(const char *const &p1,const char *const &p2)
{
return strcmp(p1,p2)>0?p2:p1;
}
int main()
{
const char *p1 = "lmy";
const char *p2 = "yml";
int val = Min(1,3);
auto res = Min<const char *>(p1,p2);
cout<<res<<endl;
return 0;
}
这样就实现了一个比较的特化版本,还是比较方便的。
需要注意的是:他看起来是函数的重载,有什么区别?
程序运行结果和使用函数模板特化相同。但是,使用普通函数重载和使用模板特化还是有不同之处,主要表现在如下两个方面:
(1)如果使用普通重载函数,那么不管是否发生实际的函数调用,都会在目标文件中生成该函数的二进制代码。而如果使用模板的特化版本,除非发生函数调用,否则不会在目标文件中包含特化模板函数的二进制代码。这符合函数模板的“惰性实例化”准则。
(2)如果使用普通重载函数,那么在分离编译模式下,需要在各个源文件中包含重载函数的申明,否则在某些源文件中就会使用模板函数,而不是重载函数。
4.非模板的形参推演
//在非模板中叫做万能引用:const int &
void fun(const int &x){cout<<"const int&"<<endl;}
//void fun(int x){cout<<" int"<<endl;}
void fun(int &x){cout<<" int&"<<endl;}
void fun(int &&x){cout<<"int &&"<<endl;}
int main()
{
int x =10;
const int y = 20;
int *p = &x;
fun(x); //1:int &,2:const int &
fun(y);//1:const int &
fun(20); //1:int && ,2:const int &
return 0;
}
引用类型未定义:
/*形参是右值引用,属于引用类型未定义*/
template <class T>
void func(T&& d) {
T y; //对于func(a)和func(b)来说报错,因为T 是引用类型。
T y = 10;//对于func(a)来说报错
}
int main()
{
int a = 10;
const int b = 20;
func(a); //T :int &, d: int & ;传入一个左值,这两个类型都是左值引用;
func(b);// T :const int &,const int &//T y=10;传入一个常性左值,这两个类型都是常性左值引用
func(20);//T int //d 是int&&
}
完美转发
void print(int &a){cout<<"int &"<<endl;}
void print(const int &a){cout<<"const int &"<<endl;}
void print(int &&a){cout<<"int &&"<<endl;}
template <class T>
void func(T&& d) {
print(std::forward<T>(d)); //完美转发 ,此时让右值保持右值的属性
}
int main()
{
int a = 10;
const int b = 20;
func(a); //T :int &, d: int & ;传入一个左值,这两个类型都是左值引用;
func(b);// T :const int &,const int &//T y=10;传入一个常性左值,这两个类型都是常性左值引用
func(20);//T int //d 是int&&
}
5.模板形参的推演
形参以值接收:
template<class T>
void func(T x) { //形参以值接收 **
}
int main()
{
int a = 10;
const int b = 20;
int& ra = a;
const int rb = b;
func(a);//T:int
func(b);//T:int
func(ra);//T:int
func(rb);//T:const int
func(&a);//T:int * 传指针,会改变实参的值,T为int *
func(&b);//T:const int *//不能改变b的值,加const
return 0;
}
形参以引用接收:
#if 0
template<class T>
void func(T &x) { //以引用接收的类型推演**
T y = 10;
}
int main()
{
int a = 10;
const int b = 20;
int& ra = a;
const int rb = b;
//func(a); //x:int &//T :int
func(b);//x:const int &//T :const int
//func(ra);//x:int & //T: int
//func(rb);//x:const int&//T:const int
//func(&a);//error 函数以引用接受,传指针报错
//func(&b);
return 0;
}
// template<class T>
// void fun( T x){} //a = 10;fun(a)->即调fun(T &x) 也调fun(T x) 调用不明确
template<class T>
void fun(T &x){cout<<"T &"<<endl;}
template<class T>
void fun(T &&x){cout<<"T &&"<<endl;}
template<class T>
void fun(const T&){cout<<"const T &"<<endl;}
int main()
{
int a = 10;
const int b = 20;
fun(a);
fun(b);
fun(20);
return 0;
}
输出:
T &
const T &
T &&