C++基础入门:模板进阶

◆博主名称:少司府
欢迎来到少司府的博客☆*: .。. o(≧▽≦)o .。.:*☆
⭐数据结构系列个人专栏:
⭐C++基础个人专栏:
⭐琢玉成器终有时,笔底生花夺锦归
目录
一、非类型模板参数
1.1 模板参数的分类
模板参数分类类型:类型形参和非类型形参
类型形参即:出现在模板参数列表中,跟在 class 或者 typename 之类的参数类型名称 。非类型形参,就是用一个常量作为类 ( 函数 ) 模板的一个参数,在类 ( 函数 ) 模板中可将该参数当成常 量来使用 。
template<size_t N = 10,bool flag = false,char s = ' '> // 非类型模板参数可以给缺省值
class Stack
{
private:
int _a[N];
int _top;
};
如图,非类型模板参数可以有缺省值,但是其只能为整型家族的值。
整型家族包括:char、int、size_t、long、long long、bool
也有例外:
// C++20支持double做模板参数
template<double D>
class A
{
private:
//...
};
C++20支持double作为模板参数。
注意:
1)、浮点数、类对象以及字符串是不允许非类型模板参数的。
2)、非类型模板参数必须在编译期就确认结果。
1.2 array静态数组
array<int, 10> a1; // 不会初始化
array<int, 100> a2;
array是C++提供的一个静态数组,相比于自定义的数组,array检查越界更严格。
array<int, 10> a1; // 不会初始化
array<int, 100> a2;
int a3[10];
// 越界检查问题
cout << a3[10] << endl; //越界读不检查,越界写检查
//a3[10] = 10;
a1[10] = 10; // 越界读写都会检查
cout << a1[10] << endl;

如图,自定义数组越界读不会报错,越界写会报错;array越界读写都会报错。
二、模板的特化
2.1 概念
通常情况下,使用模板可以实现一些与类型无关的代码(比如模板函数),但对于一些特殊类型的可能会得到一些错误的结果,因此需要做特殊处理。
在原模板类的基础上,针对特殊类型所进行特殊化的实现方 式
模板特化分为函数模板特化和类模板特化。
2.2 函数模板特化
函数模板特化的一般步骤:
1)、必须先有一个基础的函数模板
2)、关键字template后面接一对空尖括号<>
3)、函数名后跟一对尖括号,尖括号中指定需要特化的类型
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
return left < right;
}
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;
}
如图,我们可以通过模板的特化来实现对Date*的比较。当left、right类型为其他类型时,会走第一个函数模板;当其为Date*时,会走第二个。
当然,我们也可以直接使用现成的函数。
bool Less(Date* left, Date* right)
{
return *left < *right;
}
还有就是,我们在进行模板特化的时候,可能会遇到一些坑,比如:
// 函数模板 -- 参数匹配
template<class T>
bool Less(const T& left,const T& right)
{
return left < right;
}
// 不能写成const Date* left
template<>
bool Less<Date*>(Date* const& left, Date* const& right)
{
return *left < *right;
}
注意const指向的内容,特化模板要与正常模板严格匹配,这里应该是底层const,是指向的内容不能修改,而不是指针本身不能修改。
2.3 类模板特化
2.3.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;
};
2.3.2 偏特化
偏特化就是对参数列表中部分参数进行特化。
// 偏特化/半特化
// 将第二个参数特化为double
template <class T1>
class Data<T1, double>
{
public:
Data() { cout << "Data<T1, double>" << endl; }
private:
T1 _d1;
double _d2;
};
// 将第二个参数特化为char
template <class T1>
class Data<T1, char>
{
public:
Data() { cout << "Data<T1, char>" << endl; }
private:
T1 _d1;
char _d2;
};

如图,当全特化与偏特化同时存在并实例化生效时,优先使用全特化。
我们还可以对参数进行进一步的限制,例如:
//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
Data() {cout<<"Data<T1*, T2*>" <<endl;}
private:
T1 _d1;
T2 _d2;
};
针对所有类型的指针参数,都会用这个模板进行实例化。

三、模板分离编译
3.1 什么是分离编译

3.2 模板的分离编译
我们以下面情况为例:
// Func.h
void func(const int& left, const int& right);
template<class T>
T Add(const T& left, const T& right);
// Func.cpp
template<class T>
T Add(const T& left, const T& right)
{
cout << "Add(const T& left, const T& right)" << endl;
return left + right;
}
void func(const int& left, const int& right)
{
cout << "func(const int& left, const int& right)" << endl;
}
// Test.cpp
int main()
{
Add(1, 2);
Add(1.0, 2.0);
func(1, 2);
return 0;
}
如图,在链接阶段发生错误。在链接阶段,在符号表找到了func的地址,但是Add没有实例化,找不到地址。
3.3 链接错误解决方案
方法一:在Func.cpp文件显示实例化
// 显式实例化
template
int Add(const int& left, const int& right);
template
double Add(const double& left, const double& right);
方法二:直接在.h文件定义模板,用的地方就有定义,直接实例化。
四、模板优缺点总结
4.1 优点
1)、模板复用了代码,节约资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
2)、增强了代码的灵活性
4.2 缺点
1)、模板会导致代码膨胀问题,也会导致编译时间变长
2)、出现模板编译错误时,错误信息非常凌乱,不容易定位
本期的分享就到这里,如果觉得博主的文章比较对胃口的话,可以点一个小小的关注~
您的三连是我持续更新的动力~
更多推荐

所有评论(0)