之前我们学习过c++初阶模板,学习了泛型编程以及函数模板和类模板,详情可点击c++——模板初阶查看,今天我们继续学习c++进阶模板的有关内容🥳🥳
1.非类型模板形参
在初阶模板中,函数模板和类模板所传的参数前面都是class或者typename修饰的,是类类型形参,但是模板除了可以传递类类型形参之外还可以传递非类型形参
- 类型形参:出现在模板参数列表中,跟在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;
};
}
注意:
- 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
- 非类型的模板参数必须在编译期就能确认结果。
注:
2.模板的特化
2.1引例
给一个日期类:
//日期类
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;
}
2.2模板特化介绍
模板的特化是指为具体类型或具体参数提供专门的实现。通常情况下,模板的特化是为了解决某些特殊类型或参数的处理需求,使得模板能更好地适应不同的情况。
- 函数模板的特化步骤:
- 必须要先有一个基础的函数模板
- 关键字template后面接一对空的尖括号<>
- 函数名后跟一对尖括号,尖括号中指定需要特化的类型
- 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
对于上述第4点我们可以看个例子:
通常情况下比较大小得函数模板得参数应该这样给:
//函数模板
template<class t>
bool less(const t& left, const t& right)
{
return left < right;
}
那么为了使得特化之后得函数参数与之前的保持一致,特化版本参数也需要加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;
}
- const在* 右边
int main()
{
int a = 10;
const int * p = &a;
/* const 放在*的左边,修饰的是*p,表示的是指针指向的内容,不能通过指针来改变
但是指针变量本身是可以被修改的*/
int n = 20;
*p = n; // 报错 指针指向的内容不能被修改
p = &n; // 指针变量p可以修改
return 0;
}
//函数模板特化版本
template<>
bool less<date*>( date* const& left, date* const& right)
{
return *left < *right;
}
- 类模板的特化步骤:
- 必须要先有一个基础的类模板
- 关键字template后面接一对空的尖括号<>
- 类名后跟一对尖括号,尖括号中指定需要特化的类型
2.3模板特化分类
有两种类型的模板特化:全特化(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) {
// 特定类型的实现
}
3.模板分离编译
3.1 什么是分离编译
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。
3.2模板的分离编译
假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义:
// 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++程序运行一般需要经历以下步骤:
预处理——编译——汇编——链接
-
预处理:在编译之前,预处理器对源文件进行处理,包括宏展开、头文件包含等操作。
-
编译:对程序按照语言特性进行词法,语法,语义分析,检查无误之后生成汇编代码,注意头文件不参与编译,编译器对工程中的多个源文件是分离开单独编译的
-
汇编:将汇编代码翻译成机器代码。
-
链接:将编译后的目标文件与库文件进行链接,生成可执行文件。
3.3解决方法
- 将声明和定义放到一个文件 “xxx.hpp” 里面或者xxx.h其实也是可以的,这样声明定义就放在一个文件中了,推荐使用这种。
- 模板定义的位置显式实例化。这种方法不实用,不推荐使用。
显式实例化代码如下:
//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);
4.模板总结
- 优点
- 模板复用了代码,但本质上编译器会帮我们生成实例化的代码,只是减少了人工的消耗,节省资源,更快的迭代开发,c++的标准模板库(stl)因此而产生
- 增强了代码的灵活性
- 缺陷
- 模板会导致代码膨胀问题,也会导致编译时间变长
- 出现模板编译错误时,错误信息非常凌乱,不易定位错误
以上就是今天进阶模板所有的内容啦~ 完结撒花🥳🎉🎉
发表评论