在C++ 中,模板为泛型程序设计奠定了关键的基础。使用模板需要用到两个关键字template 、typename,写法:template<typename Type> template告诉编译器,将要定义一个模板,<>中的是模板参数列表,类似于函数的参数列表,关键字typename看作是变量的类型名,该变量接受类型作为其值,把Type看作是该变量的名称,是一个通用的类型。
函数模板
1.常规使用
建立一个通用函数模板,它所用到的数据的类型(包括返回值类型、形参类型、函数体中局部变量类型)用一个虚拟的类型(模板类型)来代替,而实际调用时编译器根据传入的实参来逆推出真正的类型,生成对应的具体的函数。这样一个通用的模板函数不仅将数据的值作为变化的量,类型也被参数化。
两个关键字:template:定义模板的关键字;typename :定义模板类型的关键字
其中T是模板参数,虚拟的类型,用于替换。在实际调用时,实参为int类型,那么会生成一个参数和返回值都为int类型的函数,由一个通用的模板函数生成具体类型的函数。编译器由函数模板自动生成模板函数的过程叫模板的实例化。
2.显示指定及默认值
如果函数的参数中并未使用模板类型,那么编译器无法自动推导,这时就需要手动的显示指定模板类型。
#include<iostream>
using namespace std;template<typename T>
void fun()
{T t = 0;cout << typeid(t).name() << endl; //输出变量的类型
}
int main()
{//在调用函数时,显式的指定fun<long>(); //此时模板类型T为"long"类型return 0;
}
除此之外还可以指定模板类型的默认类型(类似于函数参数指定默认值)
template<typename T = long>
void fun()
{T t = 0;cout << typeid(t).name() << endl; //输出变量的类型
}
int main()
{fun();return 0;
}
模版参数类型选择的优先级:手动显示指定>编译器根据实参自动推导>模板类型默认值
示例代码:
#include<iostream>
using namespace std;template<typename T = long>
void fun(T t)
{cout << typeid(t).name() << endl; //输出变量的类型
}
int main()
{double a = 3.14;fun(a); //doublefun<char>(a); //char
}
3.多模板参数
类似于函数参数,模板类型可以指定多个,用逗号分割,每个模板类型都需要关键字typename修饰。template<typename T,typename K> 模板类型T替换一种类型,K则可以替换为另一种类型。多模板参数同样可以根据实参进行自动推导。不同于函数参数的默认值(从右向左依次指定,不能有间断),模板参数默认值指定的顺序可以是任意的没有强制的顺序要求,但在调用函数时显式指定模板类型时必须从左向右依次指定,不能有间断。
经验:编译器能够自动推导出来的模板参数放于最后,剩余的模板参数如果有默认值的放于中间,无默认值则放于前面。
例:
template<typename T, typename K = long, typename M>
void fun(M &m);fun<double>(10); //fun<double,long,int>
fun<double, char>(10); //fun<double,char,int>
4.模板函数的声明和定义
如果函数的声明和定义分开,那么在声明和定义处都需要加上模板,如果模板存在默认类型,那么只在函数声明时指定即可。
由于模板函数的定义并不是真正的函数,他们不能单独编译,所以不能将模板函数单独放到源文件中,模板必须与特定的模板实例化请求一起使用,所以最好的办法是模板函数的声明和定义放在一起。
类模板
与函数模板差不多,类模板也需要在类定义的上面加上template 及typename,但在定义对象时,必须使用<>显式的指定模板类型。
模板类型可以替换类内的任意地方定义的类型,包括成员属性类型,成员函数。
类中成员属性若为模板类型,那么我们可以定义带参数的构造,让调用者去指定初始化值。
类模板可以有多个模板类型,且可以指定默认的模板参数,规则是从右往左依次指定不能间断(从后往前),在定义对象时从左向右指定,如果不指定模板参数,将使用默认的。
如果模板类中的成员函数在类中声明,类外实现时,函数的定义处也要加上模板,如下:
如果类模板指定了默认的类型,为了避免歧义,默认的模板类型应当去掉。
如果在模板类中声明、类外定义的成员函数存在函数模板,那么在定义的时候,两个模板都需要指定。如下:
注:这种情况下,顺序为:先类模板,后函数模板。
模板类中嵌套的情况:
#define _CRT_SECURE_NO_WARNINGS#include<iostream>
using namespace std;template<typename T>
class A{
public:T m_t;A(){m_t = 10;}A(T t) {m_t = t;}
};
class B {
public:A<long>m_a;B() {}B(long a) :m_a(a) {}
};template<typename T>
class C {
public:A<T>m_a;C(T a) :m_a(a) {}
};template<typename K>
class D {
public:K m_k;D(const K k) :m_k(k) {}
};int main()
{B b;cout << b.m_a.m_t << endl; //10B b2(30);cout << b2.m_a.m_t << endl; //30C<char>c('a');cout << c.m_a.m_t << endl; //aA<double>aa(12.3);D<A<double>>d(aa);cout << d.m_k.m_t << endl; //12.3
}