个人主页:点我进入主页
专栏分类:C语言初阶 C语言进阶 数据结构初阶 Linux C++初阶 算法
欢迎大家点赞,评论,收藏。
一起努力,一起奔赴大厂
目录
一.非类型模板参数
二.模板的特化
2.1引入
2.2全特化
2.3偏特化
三.模板的分离编译
3.1链接错误
3.2链接错误是如何产生的
3.3如何正确分离编译
3.4按需实例化
一.非类型模板参数
非类型模板参数就是模板的参数有一个常量,我们可以参考下面的代码:
template<class T, size_t N = 10>
class array
{
public :T& operator[](size_t index){return _arr[index];}int size()const{return _size;}private :T _arr[N];int _size=0;
};
在C++98中我们只支持整形的做为非类型模板参数,在后面的一些版中开始支持其他的类型作为非类型模板参数。在库中我们就有一个类似的设计array这个,我们需要知道这个非常的不好用,因为它实现的功能使用vector就可以实现,而且array在栈上开辟的空间 ,非常容易造成栈溢出,所以可以说这个基本没有任何用处。
二.模板的特化
2.1引入
class Date
{
public:friend ostream& operator<<(ostream& _cout, const Date& d);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);}
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;
}
void test1()
{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; // 可以比较,结果错误}
在日期类中我们比较当传入的是值的时候可以比较正确,但是当我i们传入指针时,我们就不能保证这个正确了,这个主要就是比较的是地址,而不是值,因此我们需要特化。
2.2全特化
template<class T>
bool Less(T left, T right)
{cout << "Less(T left, T right)" << endl;return left < right;
}
template <>
bool Less(Date* left, Date* right)
{cout << "Less(Date * left, Date * right)" << endl;return *left < *right;
}
我们需要在已经有的模板上再次写这个模板对它进行特化,但是template后面跟一个尖括号,尖括号里什么也不写,然后将类型写上,再写具体的代码。我们利用上面的代码运行可以看到:
在这里我们比较指针时会走我们的全特化,
2.3偏特化
偏特化和全特化类似,它只是特化模板的一部分,它就是对参数进一步进行限制,我们可以看下面的代码:
template <class T1,class T2>
class A
{
public :A(){cout<<"A< T1,T2>" << endl;}
private:T1 _al;T2 _a2;
};template <class T1>
class A<T1,int>
{
public:A(){cout << "A< T1,int>" << endl;}
private:T1 _al;int _a2;
};template <class T1,class T2>
class A<T1*, T2*>
{
public:A(){cout << "A< T1*, T2*>" << endl;}
private:T1* _al;T2* _a2;
};
void test1()
{A<int, char> a1;A<int, int>a2;A<int*, int*>a3;
}
运行结果为
在类的使用特化时我们需要在已有的类中再次定义这个类,但是类的后面需要加上括号,括号中的内容是类型,原来的类中的template中有几个参数它就有几个参数,特化的内容不同,没有限制的相同,特化的template中的尖括号为没有精准限制的(它不是一个模糊的,不明确的例如int等,但是参数是T*就需要将T写上)。
三.模板的分离编译
3.1链接错误
我们将一个类进行分离编译,代码如下:
//.hnamespace bit
{template<class T, size_t N = 10>class array{public:int size() const;private:T _array[N];int _size=0;};
}//.cnamespace bit
{template<class T, size_t N >int array<T,N>::size() const {return _size;}
}
我们实例化一次运行可以看到出现了链接错误
链接错误就是找不到地址,我们可以实验一下,只在.h文件中写一个func函数,调用的时候就会出现链接错误,代码如下:
//.h为
void func();
//.c直接调用
这是由于在编译时有这个声明就可以了,连接时才会进行合并call地址,call的地址就是func定义的地方,但是没有定义,所以出现链接错误。 我们将func函数的定义写好后我们看汇编代码
它会call一下地址。
3.2链接错误是如何产生的
在编译阶段开始编译器会把模板进行实例化,由于我们是模板的分离编译,所以我们会造成在.h文件中没有定义的地址,.c文件中有定义但是不知道实例化成什么
3.3如何正确分离编译
我们需要将声明和定义在同一个.h文件中,这样在编译阶段就会实例化,不需要在链接找地址,我们的代码如下:
template<class T, size_t N = 10>class array{public:int size() const;private:T _array[N];int _size=0;};template<class T, size_t N >int array<T, N>::size() const{return _size;}
3.4按需实例化
我们看代码
template<class T, size_t N = 10>class array{public:int size() const;void print(){cout << size(2);}private:T _array[N];int _size=0;};template<class T, size_t N >int array<T, N>::size() const{return _size;}
我们可以看到print函数中是错误的,但是我们不适用print这个函数,编译器就不会报错,一旦使用这个就会出错,我们看两个实验:
bit::array<int> a1;
cout<<a1.size();
bit::array<int> a1;a1.print();
造成这个结果的就是因为编译器会进行按需实例化,当我们需要的时候才会进行实例化,随着版本的更新,编译器在很久以前如果里面少写一个分号也可以编译过去,只要我们不去调用。