目录
1.函数模板是什么
1.1函数模板的基本概念
1.2函数模板的基本使用
1.3函数模板的特化
1.4非类型模板参数
2.类模板是什么
2.1类模板的基本使用
2.2非类型模板参数
2.3类模板的特化
2.4模板特化后的优先级
3.函数模板不要分离编译
1.函数模板是什么
模板是一种泛型编程,我们处理数据时可以忽略这些数据的类型。在C语言的编程当中,定义一个swap交换函数只能交换给定的参数类型的数据;但如果使用C++的模板定义函数,就可以忽略这些类型。
template <class T>
void swap(const T& x, const T& y)
{
T tmp = x;
x = y;
y = tmp;
}
1.1函数模板的基本概念
模板的关键字为 template 加一对尖括号。尖括号里面是类型(并没有指定)的名字,可以使用关键字 class 或者 typename 命名。它的作用域就是从此行开始往下找的第一个函数。当我们调用这个模板函数的时候,编译器会根据我们的实参推演出模板函数的参数类型,然后完成实例化生成一份具体的函数(也就是说,模板函数不是一个具体的函数)。
尖括号里面的参数可以是一个或者多个。如果确实需要多个参数,那么这些参数的命名不能相同(也就是说一个参数对应一个类型)。
1.2函数模板的基本使用
我们以这个函数模板为例:
template<class T1,typename T2>
void Print(const T1& x, const T2& y)
{
//获取模板参数的类型
cout << typeid(T1).name() << " " << typeid(T2).name() << endl;
}
第一种用法:让实参推演出模板的参数类型。
Print(1, 2);
Print(3.14, 2);
Print("abc", 'a');
第二种用法:显式实例化模板参数(如果实参的类型与模板参数有冲突,能进行强制类型转换就进行强制类型转换,否则报错)。
Print<int, int>(3.14, 2);
Print<string, char>("abc", 'b');
Print<string, int>(1.25, 2); //无法类型转换,报错
1.3函数模板的特化
还是以上面的函数模板为例。
如果想对某些类型进行特殊处理(例如都传入int类型的参数时,不执行打印类型的操作,而执行打印数据的值),需要用到模板的特化。特化分为全特化和半特化。
全特化的格式如下:
template<>
void Print<int,int>(const int& x, const int& y)
{
cout << "x = " << x << " y = " << y << endl;
}
函数模板没有偏特化,偏特化将会在介绍类模板时讲到。
1.4非类型模板参数
模板的参数并不一定代表某一类型,还可以作为非类型参数。
template<size_t N>
void test()
{
cout << N << endl;
}
注意调用此函数时,模板参数 N 是通过显式实例化得到的,而不是通过调用函数时传递的实参。
2.类模板是什么
没有模板之前,设计类时往往只能针对一种类型。但有了模板之后,类的设计更加泛型化。
namespace ly
{
template<class T>
class stack
{
public:
private:
T* _a; //我们可以自定义栈存储什么类型的数据
};
}
2.1类模板的基本使用
类模板的使用必须显式实例化!并且类模板的类名不代表一个类(也就是不代表某种自定义类型),只有当类模板实例化之后的类名才代表一个类(也就是代表了一种自定义类型)。
ly::stack<int> st;
ly::stack<char> st;
ly::stack<double> st;
2.2非类型模板参数
非类型模板参数用在类模板当中是一种典型用法。非类型模板参数还可以给缺省值(模板参数都可以给缺省值)。
template<class T,size_t N = 10>
class Arr
{
public:
private:
T _a[N];
};
2.3类模板的特化
类模板可以有全特化和半特化。与函数模板一样,模板根据我们显式实例化提供的参数推导出模板的类型,然后生成一份具体的类。但是如果我们想对某些类型进行特殊的处理,就可以使用特化。
我们以这个类为例:
template <class T1,class T2>
class test
{
public:
test()
{
//不管传入任何类型,最终都打印:
cout << "test<T1,T2>" << endl;
}
};
全特化的格式如下:
template <>
class test<int,int>
{
public:
test()
{
//当传入的类型都为int时,单独打印:
cout << "test<int,int>" << endl;
}
};
半特化(也叫偏特化)的格式如下:
template <class T1>
class test<T1, char>
{
public:
test()
{
//模板参数的第二个类型为char时,打印:
cout << "test<T1,char>" << endl;
}
};
2.4模板特化后的优先级
全特化>半特化>无特化。
3.函数模板不要分离编译
尽量在头文件中一起出现函数的声明和定义。因为将函数的声明放在头文件而定义放在其他源文件中会引发链接错误。
其原因就在于函数模板并不是一个真正的函数,当我们在一头文件包含了只有函数模板的声明的头文件时,编译器就会默认认为有这个函数(具体在哪由链接器去寻找),到了链接时,就会发生链接错误(因为函数模板不是函数)。