非类型模板参数
非类型模板参数介绍
模板参数分为类类型和与非类型参数,像我们之前介绍的就是类型模板参数
cpp
template<class T> //类类型模板参数
class vector
{
private:
int *_arr;
int _size;
int _capacity;
};
非类类型模板参数,就是就是用一个常量作为模板参数,比如我们要定义一个静态的栈,即栈的容量为固定值,像下面这样定义一个固定容量为100的栈
cpp
#define N 100
template<class T>
class stack
{
private:
T _arr[N];
T _top;
};
int main()
{
stack<int> st; //定义一个大小只有100的栈
return 0;
}
那要在这个的基础上面再定义一个大小为10的栈怎么办嘞,再重新定义一个,那大小为1000的嘞,再定义一个?这样是不是太不方便了,这时候就需要用到我们的非类型模板参数,像下面这样
cpp
template<class T,size_t N=100> //这里也是可以給缺省值的
class stack
{
public:
stack()
{
cout << "stack()" << endl;
}
private:
T _arr[N];
T _top;
};
int main()
{
stack<int> st; //大小为缺省值,100
stack<int, 10> st1; //定义一个大小为10的栈
stack<int, 20> st2; //定义一个大小为20的栈
return 0;
}
这就是我们的非类型模板参数,但是需要注意的是,里面的非类型模板参数不可以使用自定义类型,比如string之类的,甚至有时候浮点数也是不可以用的,比如下面
cpp
template<class T,string s>
class A
{
};
int main()
{
return 0;
}
//有的编译器再编译的时候是不会报错的,只有你在调用的时候会出错,因为按需实例化,
//加上这句话 stack<int,"1111">就会报错了
array类型介绍
array类型是对数组进行封装,使用的格式

比如
cpp
#include<array>
int main()
{
array<int, 10> arr1; //定义大小为10的数组
return 0;
}
但是为啥有了数组,还要弄一个这个对数组的封装嘞,因为普通数组对越界检查不是很严格的,

在上面的程序中,我们定义了一个大小为10的数组,但是我们越界访问了第15个位置,编译器没有报错,这就说明,普通数组对越界访问的检查并不是很严格的,但是我们的array是非常严格的

但是array的也是有缺陷的,比如下面的
cpp
#include<array>
#include<vector>
int main()
{
array<int, 10> arr;
vector<int> v(10); //定义大小为10的vector
cout << "sizeof(arr)" << sizeof(arr) << endl;
cout << "sizeof(v)" << sizeof(v) << endl;
return 0;
}
//运行结果为 40
// 32
同样容量大小的vector和array,array占用的大小更大,这就说明array设计的并不是很好,所以用array不如用vector,再这里介绍array只是当作了解,实际运用中并不会用太多
模板的特化
函数模板的特化
下面我们用一个例子来介绍今天的特化
cpp
namespace LiHao //避免和标准库中的less函数进行冲突
{
template<class T>
bool less(T x, T y)
{
return x < y;
}
}
int main()
{
int x = 10, y = 20;
int* px = &x, * py = &y;
cout << LiHao::less(x, y)<<endl; //运行结果:1
cout << LiHao::less(px, py) << endl; //运行结果:1
return 0;
}
上面的程序中,我们用指针传递指针参数是想要比较他们指向的内容,但是程序是比较了两个指针指向的地址的大小,这并不是我们的本意,想要比较他们所指向的内容的大小,该怎么办嘞,这个时候就需要用到模板的特化,可以像下面这样
cpp
template<> //这个语句是必须写
bool less<int*>(int* x, int* y)
{
return *x < *y;
}
这就是函数模板的特化,但是函数模板的特化有很多坑,比如下面的这个
cpp
template<class T>
bool less(const T& x,const T& y)
{
return x < y;
}
template<>
bool less<int*>(int* const& x, int* const& y)
{
return *x < *y;
}
如果第一个模板里面加上const的话,模板的特化也必须加上const,但是,有时候我们会加错const的位置,比如
cpp
template<class T>
bool less(const T& x,const T& y)
{
return x < y;
}
template<>
bool less<int*>(const int*& x, const int*& y) //这样是错误的,上面的const修饰的是x
{ //和y,这下面就应该修饰指针x和y,
return *x < *y; //const *p修饰的是p指向的内容,*const
} //p修饰的才是指针本身
类模板的特化
全特化是指类模板里面的参数全部都确定
部分特化是部分参数确定化
cpp
namespace LiHao
{
template<class T1, class T2>
class data
{
public:
data()
{
cout << "template<class T1,class T2>正常模式" << endl;
}
private:
T1 _data1;
T2 _data2;
};
template<class T1> //部分特化,偏特化
class data<T1, int>
{
public:
data()
{
cout << "template<class T1>偏特化" << endl;
private:
T1 _data1;
int _data2;
};
}
int main()
{
LiHao::data<double, double> d1;
LiHao::data<int, int> d2;
return 0;
}
运行结果

还可以限定类模板类型,比如下面的
cpp
template<class T1, class T2>
class data<T1*, T2*>
{
public:
data()
{
cout << "class data<T1*,T2*>" << endl;
}
};
int main()
{
Data<int *,int **> d1;
return 0;
}
运行结果

关于模板申明和定义不能分离
在之前vector和list的模拟实现当中,我们的声明和定义都是写在头文件当中的,一旦声明和定义分离,写在不同的文件当中,运行的时候会出现链接错误,现在我们来说明一下为什么会出现链接错误
我们在调用一个函数的时候,底层是去call一个函数,转到函数地址哪里去执行,函数的地址就是函数定义的地方的第一句代码的地址,如果函数定义和声明的地方不同,编译器知道函数要实例化成什么,但是没有定义,而函数定义的地方,不知道实例化成什么,因为没有被实例化而不会被编译,当然也就没有函数的地址放进符号表,因而编译器找不到函数的地址出现链接错误