模板:
优点:对类型也可以参数化了
01函数模板 02类模板
tmplate<> 定义一个模板参数列表 --》可以定义模板参数
模板参数:
1.类型参数 — typename /class 定义多个类型参数 用,号分隔
2.非类型参数 — int a(默认是个常量 不可以被修改)
函数模板/类模板
函数或者类前面有template
template<typename T> // 模板参数列表
class vector()
此处的vector是模板名称,不能直接使用,需要实例化才能使用,在实现vector模板的成员方法时使用vector的地方都要加上<T> 只有构造函数和析构函数的名字可以省略<T>。
在类外定义模板方法时,需要加上类的作用域,是vector:: 参数T需要重新定义
模板函数/模板类
是实例化以后的
1.模板不编译 编译的是实例化后的模板
模板中使用的类型替换是使用 typedef
template<typename T> // 模板参数列表
bool compare(T a, T b)
{
cout << typeid(T).name() << endl;
cout << "compare template" << endl;
return a > b;
}
int main()
{
//bool __cdecl compare<int>(int,int);
// 在函数调用点, 用int从模板实例化一份代码
// 模板的隐式实例化
compare<int>(10, 20.5);
compare<int>(20, 30);
}
以下是系统自己根据传入的类型参数实例化出的模板函数:
/*
模板函数
bool compare<int>(int a, int b)
{
cout << "compare template" << endl;
return a > b;
}
bool compare<double>(double a, double b)
{
cout << "compare template" << endl;
return a > b;
}
bool compare<const char*>(const char* a, const char* b)
{
cout << "compare template" << endl;
return a > b;
}
*/
2.模板的实参推演 编译器可以根据传入的实参类型,推导出来形参类型T
template<typename T> // 模板参数列表
bool compare(T a, T b)
{
cout << typeid(T).name() << endl;
cout << "compare template" << endl;
return a > b;
}
int main()
{
// const char *
// 模板的实参推演
compare("aaa", "bbb");
虽然没有传入类型 但是模板会根据实参"aaa"推演出该用const char 类型(字符串是常量)
bool compare<const char*>(const char* a, const char* b)
{
cout << "compare template" << endl;
return a > b;
}
但是这里return a>b;比较字符串的地址 这是错误的,所以有时候编译器自己推出来的会出错,需要特例化一下!
注意:模板只是省了开发者的活,但是没有省编译器的活.编译器仍然会自动实例化模板函数
3.针对以上问题,我们要自己实例化一个模板函数,这就叫做针对char*类型的特例化函数(本来是编译器自己实例化的)
template<>//括号里空着是因为是特例化所以已经知道调什么类型 就不写了
bool compare<const char*>(const char*a, const char*b)
{
cout << "compare<const char*>" << endl;
return strcmp(a, b) > 0;//按照字母的前后顺序比较
}
ps:
模板和特例化必须保持模板名相同,参数形式形同
模板会在调用地方实例化出来对应的函数或者类
模板内的成员方法如果没有被使用也不会被编译
模板的实例化在编译期
4.写成如下代码,优先理解为函数名,然后才是模板
compare("aaa", "bbb");
写成如下代码,优先理解为模板,然后才是函数名
compare<const char*>("aaa", "bbb");
5.模板的代码不能定义在源文件中,然后在另外的源文件中调用,它都是放在头文件中,源文件用#include导出来(可以导头文件)
因为:模板代码不进行编译 不产生符号(如果模板的代码不在这个调用的源文件里,那就只是无符号的,那就没法用)
// compare<int> *UND*
// compare *UND*
// compare<const char*> *UND*
解决方法:
tip1: 模板的显示实例化
(没什么意思,只是在模板所在的源文件先实例化了要用的类型,然后就生成了符号,从而在调用点的源文件里找的时候就能找到)
template<typename T> // 模板参数列表
bool compare(T a, T b)
{
cout << typeid(T).name() << endl;
cout << "compare template" << endl;
return a > b;
}
// 模板的显示实例化
template bool compare<int>(int, int);
tip2: 模板的隐式实例化
(大多用的是这个方法,就是都在一个源文件/模板在头文件里,不知道实例化过没,没的话就给他实例化)
template<typename T> // 模板参数列表
bool compare(const T a, const T b)
{
cout << typeid(T).name() << endl;
cout << "compare template" << endl;
return a > b;
}
......
compare<int>(10, 20.5);
compare<int>(20, 30);
6.模板的特例化必须在函数名 函数的参数形式上保持一致
函数的原模板和特例化模板必须在同一作用域
对于模板中使用的类型替换使用typedef
template <typename T>
class Test{
public:
void print(){
cout << "General template object" << endl;
}
};
template<> // 对int型特例化
class Test<int>{
public:
void print(){
cout << "Specialized template object" << endl;
}
};
int main()
{
Test<int> a;
Test<char> b;
Test<float> c;
a.print();
b.print();
c.print();
return 0;
}
输出结果:
Specialized template object
General template object
General template object
7.参数匹配都以最佳匹配为原则。
特例化的本质是实例化一个模板,而非重载它。特例化不影响参数匹配。
例如,下面如果是compare(3,5),则调用普通的模板,若为compare(“hi”,”haha”)则调用特例化版本(因为这个cosnt char*相对于T,更匹配实参类型),注意,二者函数体的语句不一样了,实现不同功能。
template<typename T> //函数模板
int compare(const T &v1,const T &v2)
{
if(v1 > v2) return -1;
if(v2 > v1) return 1;
return 0;
}
//模板特例化,满足针对字符串特定的比较,要提供所有实参,这里只有一个T
template<>
int compare(const char* const &v1,const char* const &v2)
{
return strcmp(p1,p2);
}