在初阶模板中,函数模板和类模板所传的参数前面都是class或者typename修饰的,是类类型形参,但是模板除了可以传递类类型形参之外还可以传递非类型形参
模板参数分类类型形参和非类型形参
常量前面加上自己的类型
例如定义一个静态数组的类,其大小是固定的就可以传递非类型模板参数:
namespace tutu
{
// 定义一个模板类型的静态数组
template<class T, size_t N = 10>
class array
{
public:
T& operator[](size_t index)
{
return _array[index];
}
const T& operator[](size_t index)const
{
return _array[index];
}
size_t size()const
{
return _size;
}
bool empty()const
{
return 0 == _size;
}
private:
T _array[N];//非类类型模板参数,N=10
size_t _size;
};
}
注意:
非类型模板参数必须是整数类型、枚举类型或指向对象的指针或引用类型。这是因为在编译时,非类型模板参数需要在编译器确定其值,而浮点数、类对象以及字符串在编译时无法确定其值。
注:
C++20之前,只允许整型做非类型模板参数 C++20之后,支持浮点数等其他内置类型
引例
给一个日期类:
//日期类
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
bool operator<(const Date& d)const
{
return (_year < d._year) ||
(_year == d._year && _month < d._month) ||
(_year == d._year && _month == d._month && _day < d._day);
}
bool operator>(const Date& d)const
{
return (_year > d._year) ||
(_year == d._year && _month > d._month) ||
(_year == d._year && _month == d._month && _day > d._day);
}
friend ostream& operator<<(ostream& _cout, const Date& d);
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
我们可以写一个函数模板来比较不同类型的大小,例如比较日期类的大小
//函数模板
template<class T>
bool Less(T left, T right)
{
return left < right;
}
当我们使用日期类对象比较时,发现可以借用类对象中重载的<来比较,但是如果我们使用日期类对象的指针比较日期大小时就可能得不出我们想要得结果,代码如下:
int main()
{
cout << Less(1, 2) << endl; // 可以比较,结果正确
Date d1(2024, 6, 6);
Date d2(2024, 12, 14);
cout << Less(d1, d2) << endl; // 可以比较,结果正确
Date* p1 = &d1;
Date* p2 = &d2;
cout << Less(p1, p2) << endl; // 可以比较,结果错误
return 0;
}
那么如果我们想要通过日期类的指针比较其所指向日期的大小时,就需要对它解引用再比较,这时上述的比较日期大小的函数模板就不适用了
我们就可以使用函数模板的特化,当使用日期类指针进行日期比较时就使用特化版本,特化代码如下:
//函数模板
template<class T>
bool Less(T left, T right)
{
return left < right;
}
//函数模板特化版本
template<>
bool Less<Date*>( Date* left, Date* right)
{
return *left < *right;
}
这样上述使用日期类指针比较日期的函数就可以使用特化版本来实现比较了
模板的特化是指为具体类型或具体参数提供专门的实现。通常情况下,模板的特化是为了解决某些特殊类型或参数的处理需求,使得模板能更好地适应不同的情况。
注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出。该种实现简单明了,代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化时需要特别给出,因此函数模板不建议特化。
对于上述第4点我们可以看个例子: 通常情况下比较大小得函数模板得参数应该这样给:
//函数模板
template<class T>
bool Less(const T& left, const T& right)
{
return left < right;
}
需要加const修饰,一般使用传引用传参
那么为了使得特化之后得函数参数与之前的保持一致,特化版本参数也需要加const修饰和引用:
//函数模板特化版本
template<>
bool Less<Date*>( const Date*& left,const Date*& right)
{
return *left < *right;
}
运行时却发现报错了:
这是因为const在修饰指针时有两种情况:
int main()
{
int a = 10;
const int * p = &a;
/* const 放在*的左边,修饰的是*p,表示的是指针指向的内容,不能通过指针来改变
但是指针变量本身是可以被修改的*/
int n = 20;
*p = n; // 报错 指针指向的内容不能被修改
p = &n; // 指针变量p可以修改
return 0;
}
int main()
{
int a = 10;
const int * p = &a;
/* const 放在*的左边,修饰的是*p,表示的是指针指向的内容,不能通过指针来改变
但是指针变量本身是可以被修改的*/
int n = 20;
*p = n; // 报错 指针指向的内容不能被修改
p = &n; // 指针变量p可以修改
return 0;
}
上述函数模板中const修饰的是变量本身,所以对于特化版本来说,指针的修饰也应该是自己本身,所以我们应该使用第二种情况:
//函数模板特化版本
template<>
bool Less<Date*>( Date* const& left, Date* const& right)
{
return *left < *right;
}
使用类模板特化:使用类模板特化时,需要明确指定特化的类型。使用特化的方式与使用普通的类一样,只是在使用时需要提供特化的类型。
有两种类型的模板特化:全特化(full specialization)和偏特化(partial specialization)。
template <> // < > 中为空表示全特化
return_type function_name<specific_type>(parameters) {
// 特定类型的实现
}
template <specific_type1, specific_type2, ...> // < > 中为具体类型
return_type function_name<generic_type<specific_type1, specific_type2>>(parameters) {
// 特定类型的实现
}
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。
假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义:
// a.h
template<class T>
T Add(const T& left, const T& right);//声明
// a.cpp
template<class T>
T Add(const T& left, const T& right)//定义
{
return left + right;
}
// main.cpp
#include"a.h"
int main()
{
Add(1, 2);
Add(1.0, 2.0);
return 0;
}
运行时就会出现以下情况:
这是因为C/C++程序运行一般需要经历以下步骤: 预处理——编译——汇编——链接
//a.cpp
#include"a.h"
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
//显示实例化
template
int Add(const int& left, const int& right);
template
double Add(const double& left, const double& right);
以上就是今天进阶模板所有的内容啦~ 完结撒花🥳🎉🎉