一、非类型模板参数
非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。
非类型模板参数满足俩点:1、常量 2、必须是整形
template<class T,size_t N>
class Stack
{
public:
void fun()
{
N == 0;
}
private:
T _a[N];
int _top;
};
int main()
{
Stack<int, 10>s;
Stack<int, 100>s1;
s.fun();
return 0;
}
注:1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
2. 非类型的模板参数必须在编译期就能确认结果。
二、模板特化
模板特化(Template Specialization) 是对模板的一种特殊化机制,用于根据不同的数据类型或模板参数,提供不同的实现。模板特化允许你为某个特定类型提供一个定制化的实现,而不是使用通用的模板定义
//
template<class T>
bool Less(T left, T right)
{
return left < right;
}
int main()
{
cout << Less(1, 2) << endl; // 可以比较,结果正确
Date d1(2022, 7, 7);
Date d2(2022, 7, 8);
cout << Less(d1, d2) << endl; // 可以比较,结果正确
Date* p1 = &d1;
Date* p2 = &d2;
cout << Less(p1, p2) << endl;//可以比较,结果错误
return 0;
}
在上述代码中,p1指向的d1对象明显小于p2指向的对象d2,但是Less内部没有比较这俩内容,而比较的是所指向的地址,因而导致错误,这时需要对模板进行特化。模板特化分为函数模板特化和类模板特化
2.1函数模板特化
函数模板需注意:
- 必须要先有一个基础的函数模板
- 关键字template后面接一对空的尖括号<>
- 函数名后跟一对尖括号,尖括号中指定需要特化的类型
- 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
return left < right;
}
// 对Less函数模板进行特化
template<>
bool Less<Date*>(Date* left, Date* right)
{
return *left < *right;
}
int main()
{
cout << Less(1, 2) << endl; // 可以比较,结果正确
Date d1(2022, 7, 7);
Date d2(2022, 7, 8);
cout << Less(d1, d2) << endl; // 可以比较,结果正确
Date* p1 = &d1;
Date* p2 = &d2;
cout << Less(p1, p2) << endl;//调用特化之后的版本
return 0;
}
2.2类模板特化
2.2.1全特化
全特化就是将模板参数列表中所有的参数都确定化
template<class T1, class T2>
class Data
{
public:
Data() {cout<<"Data<T1, T2>" <<endl;}
private:
T1 _d1;
T2 _d2;
};
template<>
class Data<int, char>
{
public:
Data() {cout<<"Data<int, char>" <<endl;}
private:
int _d1;
char _d2;
};
void Test()
{
Data<int, int> d1;
Data<int, char> d2;
}
2.2.2偏特化
偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。偏特化主要有以下俩中表现方式
- 部分特化
//将第二个参数特化为double
template<class T1>
class Data<T1, double>
{
public:
Data() { cout << "data<T1, double>" << endl; }
private:
T1 d1;
};
- 参数更进一步的限制
偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版
本。
//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
Data() {cout<<"Data<T1*, T2*>" <<endl;}
private:
T1 _d1;
T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
Data(const T1& d1, const T2& d2)
: _d1(d1)
, _d2(d2)
{
cout<<"Data<T1&, T2&>" <<endl;
}
private:
const T1 & _d1;
const T2 & _d2;
};
void test2 ()
{
Data<int,double> d1; //调用特化double型
Data<int , double> d2; // 调用基础的模板
Data<int *, int*> d3; // 调用特化的指针版本
Data<int&, int&> d4(1, 2);//调用引用版本
}
模板总结
【优点】
- 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
- 增强了代码的灵活性
【缺陷】
- 模板会导致代码膨胀问题,也会导致编译时间变长
- 出现模板编译错误时,错误信息非常凌乱,不易定位错误