如果没有了解过模板的朋友,就去看一下我的模板初阶文章:C++之模板初阶-CSDN博客
通过模板我们可以尝试去实现泛型编程,模板分为函数模板和类模板
那么下面我会跟大家介绍一下,模板进阶的知识
非类型模板参数
模板参数被分为:类型形参和非类型形参
类型形参:出现在模板参数列表中,跟在class或者typename之类的参数类型名称
非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用
我们先看一个例子,我们写个静态栈结构:
#define N 10
template<class T>//类型模板参数
class Stack
{
private:
T _a[N];
size_t _top;
};
int main()
{
Stack<int> st1;//大小为10
Stack<int> st2;
}
我们想要去改变栈的大小就修改我们的宏就好了,如果我们有两个栈呢?一个栈想要10,另一个想要1000的大小,这样子我们就不能满足多个栈的需求了,除非我们在定义一个类模板,但是这样子会减少我们的效率,所以,在C++当中,模板有一个非类型的模板参数概念:
template<class T,size_t N>
//T是类型模板参数,N是非类型模板参数,N是一个常量
class Stack
{
private:
T _a[N];
size_t _top;
};
int main()
{
Stack<int,100> st1;//100
Stack<int,20000> st2;//20000
}
我们这样子就可以通过传参来完成我们每个栈想要的大小需求了,在template<class T,size_t N>当中,T是类型模板参数,这里的N是非类型模板参数,这里的N是一个常量
那我们可以这样去实现传参嘛?
int main()
{
static int n;
cin>>n;
Stack<int,n> st;//error,非类型模板参数不能是变量
return 0;
}
这样子在语法上不允许的,因为非类型传参不能是变量
在STL中的容器当中,C++11新增了一个array容器,array这个容器就是类似这样的结构,它使用了非类型的模板参数:
template<class T,size_t N>
class Array
{
private:
T _a[N];
}

array是一个大小固定的容器
但是array这个容器我们一般不建议去使用
因为函数调用会建议栈帧,数组过大,可能会造成栈溢出,用vector了话,空间不够就增容,比较灵活,增容是在堆区中开辟空间,而堆区时进行动态开辟的地方,它的空间比较大,知道需要的数据大小直接使用vector中的resize就好了,没必要使用array这个容器,所以我们可以知道C++11增加的array这个容器基本没有什么用,它的缺点大于他的优点
非类型模板参数缺省值
模板参数都可以给缺省值,模板参数给缺省值和函数参数给缺省值是完全相似的,可以全缺省,也可以半缺省(必须从右往左连续缺省)
比如:
//模板参数都可以给缺省值
//模板参数给缺省值和函数参数给缺省值是完全类似的
//可以全缺省
//也可以半缺省 — 必须从右往左连续缺省
template<class T,size_t N = 10>
class Array
{
private:
T _a[N];
}
int main()
{
Array<int> a1;
Array<int,20> a2;
return 0;
}
注意:如果全都是缺省值的时候不能创建这样的对象:
Array a1;
全部都是缺省值,我们可以不传参数,但是我们知道Array是个模板,模板也是有类型的,我们需要这样:
Array<> a1;
注意:
1、浮点数、类对象以及字符串是不允许我们作为非类型模板参数的
template<class T,string s1>
template<class T,double s1>
2、非类型的模板参数必须在编译期就能确认结果
模板的特化
概念
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果
template<class T>
bool IsEqual(const T& left,const T& right)
{
return left==right;
}
int main()
{
cout<<IsEqual(1,2)<<endl;
char p1[] = "hello";
char p2[] = "hello";
cout<<IsEqual(p1,p2)<<endl;//数组名是指针常量
return 0;
}
这个模板可以用来比较整形,但是比较地址就会出现问题
此时可以使用模板的特化,可以针对某些类型进行特殊化去处理,我们可以这样写
bool IsEqual(const char*& left,const char*& right)
{
return strcmp(left,right)==0;
}
int main()
{
cout<<IsEqual(1,2)<<endl;
char p1[] = "hello";
char p2[] = "hello";
cout<<IsEqual(p1,p2)<<endl;//数组名是指针常量
return 0;
}
我们调式过后,发现这里不会进这个函数,因为数组名是指针常量,则这里的const修饰的是*left,是left指向的内容不能修改,而不是left不能修改,这里属于权限放大了
需要这样改,这样就可以进去了:
//模板的特化,针对某些类型进行特殊化处理
bool IsEqual(const char*& const left,const char*& const right)
{
return strcmp(left,right)==0;
}
bool IsEqual(const char* left,const char* right)
{
return strcmp(left,right)==0;
}
函数模板的特化
函数模板的特化步骤:
template<class T>
void Swap(T& a,T& b)
{
//vector代价太大
T tmp = a;
a = b;
b = tmp;
}
int main()
{
int x = 1;
int y = 2;
Swap(x,y);
vector<int> v1 = {1,2,3,4};
vector<int> v2 = {10,20,30,40};
Swap(v1,v2);
return 0;
}
我们交换的类型是vector时,此时用模板函数进行交换代价太大了,一次拷贝构造+两次赋值重载,所以我们可以优化一下:
//函数模板的特化
template<>
void Swap<vector<int>>(vector<int>& a,vector<int>& b)
{
a.swap(b);
}
当然也可以这样,利用模板的匹配原则,进行特殊化处理:
//模板的匹配原则,进行特殊化处理
void Swap(vector<int>& a,vector<int>& b)
{
a.swap(b);
}
类模板的特化
全特化
全特化就是将模板列表中所有的参数都确定话
template<class T1,class T2>
class Data
{
public:
Data() { cout << "Data<T1,T2>"<<endl; }
private:
T1 _d1;
T2 _d2;
};
//全特化
template<>
class Data<double, double>
{
public:
Data() { cout << "Data<double,double>" << endl; }
private:
T1 _d1;
T2 _d2;
};
int main()
{
Data<int,int> d1;
Data<double,double> d2;
return 0;
}

偏特化
偏特化:任何针对模板参数进一步进行条件限制设计的特化版本
偏特化有两种:
1、部分特化,将模板参数类表中的一部分参数特化
template<class T1,class T2>
class Data
{
public:
Data() { cout << "Data<T1,T2>"<<endl; }
private:
T1 _d1;
T2 _d2;
};
//偏特化或者半特化
template<class T1>
class Data<T1,double>
{
public:
Data() { cout << "Data<T1,double>" << endl; }
};
int main()
{
Data<int,double> d2;
Data<char,double> d1;
return 0;
}

2、参数更进一步的限制,偏特化并不是仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本
//必须要存在原模板,在原模板的基础上去特化
template<class T1,class T2>
class Date
{
public:
Date() {
cout << "Date<T1,T2>" << endl;
}
private:
T1 _d1;
T2 _d2;
};
//偏特化,传的类型是指针
template<typename T1,typename T2>
class Date<T1*, T2*>
{
public:
Date()
{
cout << "Date<T1*,T2*>" << endl;
}
};
//传的是引用
template<typename T1, typename T2>
class Date<T1&, T2&>
{
public:
Date()
{
cout << "Date<T1&,T2&>" << endl;
}
};
//也可以半指针,半引用
template<typename T1, typename T2>
class Date<T1&, T2*>
{
public:
Date()
{
cout << "Date<T1&,T2*>" << endl;
}
};
int main()
{
Date<char*, char*> d5;
Date<int*, double*>d6;
Date<int&, double&>d7;
Date<int&, double*>d8;
return 0;
}

网硕互联帮助中心





评论前必须登录!
注册