模板的意义:对类型也可以进行参数化了
template<typename T,typename E>//定义一个模板参数列表,typename也可以用class替换
bool compare(T a,T b){//compare是一个函数模板
cout<<"template compare"<<endl;
return a>b;
}
//特例化:
template<>
bool compare<const char*>(const char*a,const char*b){
cout<<"compare<const char*>"<<endl;
return strcmp(a,b)>0;//比较ASCII码顺序
}
bool compare(const char*a,const char*b){//#4没有尖括号就是一个普通函数,与模板没有任何关系
cout<<" normal compare"<<endl;
return strcmp(a,b)>0;
}
int main(){
//#1函数调用点
/*bool compare<int>(int a,int b){
cout<<"template compare"<<endl;
return a>b;
}*/
compare<int>(1,2);//compare<int>模板名加参数列表合起来才是函数名
compare(2,3);//#2只用模板名也可以正常调用
compare(2,3.0);//实参推演不成功报错,解决方法1:指定类型,解决方法2:compare函数模板的形参列表用不同的模板参数(T a,E b)
compare("aaa","bbb");//调用普通函数
/*如果没有写特例化和普通函数便会实例化出:
compare<const char*>(const char* a,const char* b){
cout<<"template compare"<<endl;
return a>b;#3虽然成功运行,不过比较的是地址顺序,没有意义
}*/
compare<const char*>("aaa","bbb");//调用特例化模板函数
return 0;
}
#1在函数调用点,编译器根据用户指定的类型,从原模板实例化出一份函数代码
实例化出的函数称为模板函数,是真正参与代码编译的函数。函数模板本身无法编译
#2只用模板名也可以正常调用,是模板的实参推演,根据传入的实参类型,来推导出模板类型参数的具体类型。同一种类型,模板只会实例化一次
#3对于某些类型,以来编译器默认实例化的模板代码,代码处理逻辑会有错误,此时需要给模板提供特例化(特殊的实例化),实例化时的模板代码不由编译器提供,而是由用户提供
#4参数列表相同的模板函数、特例化函数、普通函数(构成重载的说法不严谨,因为函数名并不相同,模板函数函数名还包括尖括号),调用优先级:普通函数>特例化>模板函数,不写尖括号优先调用普通函数,再是特例化,再是实参推演,因为编译器优先将compare处理成函数名,找不到对应函数才找函数模板;写了尖括号先调用特例化,再是普通模板函数
函数不会去调用参数列表不同的函数,比如参数是int类型的会去自动调用模板函数并实参推演,而不是调用形参是const char*类型的普通函数
分文件定义声明的情况
模板函数.cpp
#include <iostream>
using namespace std;
template<typename T>
bool compare(T a, T b) {
cout << "template compare" << endl;
return a > b;
}
template<>
bool compare<const char*>(const char* a, const char* b) {
cout << "compare<const char*>" << endl;
return strcmp(a, b) > 0;
}
bool compare(const char* a, const char* b) {
cout << " normal compare" << endl;
return strcmp(a, b) > 0;
}
test.cpp
#include <iostream>
using namespace std;
template<typename T>
bool compare(T a, T b);
int main() {
//compare(1,2);会报错,因为在其他文件找不到名为compare<int>的函数,因为模板函数不会编译,实例化后才编译
compare("aaa","bbb" );
compare<const char*>("aaa", "bbb");
return 0;
}
//结果是都调用了特例化compare<const char*>
#include <iostream>
using namespace std;
template<typename T>
bool compare(T a, T b);
bool compare(const char* a, const char* b);
int main() {
compare("aaa","bbb" );//调用普通函数compare
compare<const char*>("aaa", "bbb");//调用了特例化compare<const char*>
return 0;
}
总结:模板代码是不能在一个文件中定义,在另外一个文件中使用。模板代码调用之前一定要看到它这个函数名定义的地方,这样模板才能正常实例化,产生能被编译器编译的代码。
所以模板代码一般放在.h文件中,然后在源文件中#include包含
如果一定要分文件,需要在模板函数.cpp中:
#include <iostream>
using namespace std;
template<typename T>
bool compare(T a, T b) {
cout << "template compare" << endl;
return a > b;
}
//和特例化有区别,此操作为告诉编译器直接执行指定类型的实例化,不再在调用点实例化
template bool<int>(int,int);
template bool<double>(double,double);
模板的非类型参数
必须是整数类型(int,char,short,long)(整数或地址、引用),都是常量,只能使用,不能修改
#include<iostream>
using namespace std;
template<typename T,int SIZE>
void sort(T* arr) {
for (int i = 0; i < SIZE - 1; ++i) {
for (int j = 0; j < SIZE - 1 - i; ++j) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j + 1] = tmp;
}
}
}
}
int main() {
int arr[] = { 1,4,235,64,23,7,5 };
const int size = sizeof(arr) / sizeof(arr[0]);
//使用方式:
sort<int, size>(arr);
for (int val : arr) {
cout << val << " ";
}
cout << endl;
return 0;
}