1,基本知识
先了解下C++的引用作为函数返回值的情况;
引用作为函数返回值,不产生值的副本,提高效率。
2,初识函数模板
#undef __STRICT_ANSI__
#include<iostream>
using namespace std;
template<typename T>
T const& max_element(const T* var,size_t size){//引用作为函数返回值
T const *res=var;//声明一个指向常量的指针
for(size_t i=1;i<size;i++){
if(var[i]>*res) res=&(var[i]);//注意不能对*res直接赋值,但是可以改变指针res本身的内容(即地址信息)
}
return *res;
}
int main(){
int l[]={2,0,1,1,0,8,2,5};
char cc[]="August";
cout<<max_element<int>(l,8)<<endl;<span style="color:#ff6666;">//输出8</span>
cout<<max_element<char>(cc,6)<<endl;<span style="color:#ff0000;">//输出u</span>
return 0;
}
如果取消<>中的内容,也是可以正常输出的。
cout<<max_element(l,8)<<endl;<span style="color:#ff6666;">//输出8</span>
cout<<max_element(cc,6)<<endl;<span style="color:#ff0000;">//输出u</span>
因为模板参数可以自动推导出来;
注意的是编译器只是根据函数调用时给出的实参列表来推导模板参数值,与函数参数无关的类型无法推导。
还有一些其他情况,如果引起混淆或者需要费力推导的,都是不可以自动推导的。
所以建议复杂情况还是把参数类型写出来比较好。
2.1 模板函数的静态变量
template<typename T0,
typename T1,
typename T2,
typename T3>
void func(T1 v1,T2 v2,T3 v3){
T0 static sv0=T0(0);
cout<<"\tv1:"<<v1
<<"\tv2:"<<v2
<<"\tv3:"<<v3
<<"\t||sv0: "<<sv0<<endl;
sv0--;
}
int main(){
double sv4;
func<int,int,int>(1,2,3);//和下面的一个完全一样
func<int,int,int>(1,2,3);
func<double,int,int>(1,0.1,0.1);
func<int,double,double>(0.1,0.1,0.1);
return 0;
}
直接看结果中的最右边一列。sv0是模板函数中的静态变量,与输入无关。但是第二个函数的sv0输出是-1,说明sv0做了运算。
第一个和第二个函数模板参数完全相同,是不是可能两个函数用的同一个函数体呢?确实是这样的。
编译器为同一组模板参数值只生成唯一的函数实体。
3,函数模板和普通函数的使用不同
普通函数一般讲定义放在头文件,实现放在主文件。
但是这对于函数模板的函数体是不可以的。
模板函数声明://max.hpp
template<typename T>
T const& max_element(const T* var,size_t size);
它的实现://max.cpp
template<typename T>
T const& max_element(const T* var,size_t size){//引用作为函数返回值
T const *res=var;//声明一个指向常量的指针
for(size_t i=1;i<size;i++){
if(var[i]>*res) res=&(var[i]);//注意不能对*res直接赋值,但是可以改变指针res本身的内容(即地址信息)
}
return *res;
}
#include"max.hpp"
int main(){max_element(....)};
单独编译都没问题,但是连接两个目标文件链接器会报错。
因为只是将max.hpp文件包含进来,只有函数声明无具体实现,编译器无法生成函数模板实例.
结论就是:把函数模板的函数体声明和实现放在同一个文件中。
另外要运用命名空间来避免重复的模板实例的实现。
参考《深入实践C++模板编程》