模板的参数一定是类型吗?答案:不是。
模板参数:
举例:
我们可以搞一个模板类型的静态数组。
template <class T, size_t n = 10>
class my_array
{
public:
my_array()
:_size(n)
{
}
T& operator [](size_t index)
{
return My_array[index];
}
size_t size()
{
return _size;
}
bool empty()
{
return _size == 0;
}
private:
T My_array[n];
size_t _size;
};
这个数组的默认大小我设置为10,当然我们可以自己来定义数组的大小,
// 默认是10
my_array<int> a;
// 传模板参数100
my_array<int,100> b;
我们也可以验证一下,如果非类型模板参数我们不设置为常量,而是设置成float,看看会是什么情况
c++20,会支持非类型模板参数为float,之前的版本都不支持的。
模板的特化:
特化的目的是实现特殊的功能,如果按照原本模板的推导,并不能实现我们预期的功能,这就需要我们进行模板特化。
定义:
模板特化(template specialization)不同于模板的实例化,模板参数在某种特定类型下的具体实现称为模板的特化。 模板特化有时也称之为模板的具体化,分别有函数模板特化和类模板特化。
比如我们要进行一个比大小的功能,如果传的是int ,long 等都还好说,要是传的是一个int* 指针,我们改怎么操作呢?比地址的高低是没有意义的,所以我们要去比 int*所以指向的内容。
template<class T>
bool compare(T& x,T& y)
{
return x > y;
}
这就是一个简单的比较大小的函数模板:
int main()
{
cout << compare(10, 2) << endl;
cout <<compare(2, 10)<<endl;
int a = 10;
int b = 2;
int* x = &a;
int* y = &b;
cout << compare(a, b) << endl;
}
前俩个compare()是不需要函数特化的,模板去推导的就满足功能;但是最后的compare(),比较的是指针,模板推导比的是地址的高低,我想要去比较指针所指向的内容,该怎么办?模板特化。
template<class T>
bool compare( T x, T y)
{
return x > y;
}
template<>
bool compare<int*>(int* x,int* y)
{
return *x > *y;
}
这就是函数模板的特化。
函数模板特化的使用规则:
- 必须要先有一个基础的函数模板
- 关键字template后面接一对空的尖括号<>
- 函数名后跟一对尖括号,尖括号中指定需要特化的类型
- 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪错误
类模板的特化分类:
(1) 全特化
我拿下面的类进行举例:
template<class T1,class T2>
class two_val
{
public:
two_val()
{
cout << "" << endl;
}
private:
T1 x;
T2 y;
};
全特化为类模板的参数全部确定化
template<>
class two_val<char,int>
{
public:
two_val()
{
cout << "" << endl;
}
private:
char x;
int y;
};
我们来创建类对象:
int main()
{
two_val<int, int> s;
two_val<char, int> ss;
}
(2) 偏特化
偏特化首先可以只特化一部分:
以下两种都是偏特化:
template<class T1>
class two_val<T1,int>
{
public:
two_val()
{
cout << "" << endl;
}
private:
T1 x;
int y;
};
template<class T2>
class two_val<char,T2>
{
public:
two_val()
{
cout << "" << endl;
}
private:
char x;
T2 y;
};
偏特化其次是一种对特化的进一步限制:
比如:将模板参数,特化成指针 / 特化成引用,这也是一种偏特化
template<class T1, class T2>
class two_val<T1*,T2*>
{
public:
two_val()
{
cout << "" << endl;
}
private:
T1* x;
T2* y;
};
为什么要分离编译?我们在做一个较大的项目时,多人协作,头文件一般用于看框架,源文件去实现功能,这个功能的实现需要多人协作,每个人负责实现一部分的功能。
但是模板声明和定义分开的话,会导致链接错误。这是因为分开定义导致模板函数或类,没有实例化。
我可以演示一下:
以一个加法模板函数为例:
add.h头文件:
#pragma once
template <class T>
T add(T x, T y);
add.c源文件:
#include"add.h"
template <class T>
T add(T x, T y)
{
return x + y;
}
main函数源文件:
#include"add.h"
int main()
{
add(1, 2);
return 0;
}
我们看看运行情况:
很明显的链接错误、
该如何解决呢?
add.h头文件:
#pragma once
template <class T>
T add(T x, T y);
template
int add<int>(int x, int y);
这样就支持了 int类型的加和。如果想要支持double类型的加和呢?还得手动实例化,这种方法是比较累人的。
这种方法是常用的,定义在头文件里就好了,简单省事:
#pragma once
template <class T>
T add(T x, T y);
template <class T>
T add(T x, T y)
{
return x + y;
}
类模板和函数模板是一样的。
优点:
缺陷: