一个偶然的机会,让我接触到了泛式编程,它的美妙简直让我沉迷其中,短时间的学习,让我喜欢上了这个方法,它的出现让我想到了之前许多不理解的地方,C++中的STl库非常频繁的使用了它,因此特地去学习了一下
/*-----------------对template的学习(泛型编程)-------------------------------*/
作为一个强制语言,它的数据类型在一开始就固定了,因此要编写一段通用的逻辑,可以把任意类型的变量传去处理。
泛型编程 把通用逻辑设计为模板,摆脱了类型的限制,提升了代码的可重用性
模板定本身不参与编译,而是编译器根绝模板的用户使用模板时提供的参数生成代码,再进行编译
在现在总结学习template的时候,因为已经有一些C++基础了,因此就直接回用一些STl举例子,因为它里面运用的template是非常丰富的!
这个例子纯属个人YY,如果有大佬认为不对,方便的话请告诉我,评论即可!
例如常用的排序:sort(const T*,len,cmp);
它里面第三个参数,cmp用的就是泛型 ,因为我们使用这个函数的时候,数据类型是很多的,int,ll,string,struct什么的,因此,为了调和这些数据类型之间的冲突,用到了泛型编程template。
现在声明一下,下面的代码我已经在我的编译器上测试过了,可以使用,但是有些我又添了些其他的,因此直接使用的时候要注意注释掉一些代码,否则可能会引起冲突
现在,我们写一个比较的函数,穿入a,b,a大返回1,b大返回-1,相等返回0;
template<typename T>//简单的泛型比较
int compare(const T &left,const T &right)
{
if(left!=right)
return left>right?1:-1;
else
return 0;
}
int main()
{
//将函数模板复制给一个指定类型的函数指针,
//让编译器根据这个指针的类型,对实参进行推断
//int (*pf) (const int &,const int &)=compare;//推断T的类型为int
cout<<compare<string>("b","a")<<endl;//这两种的输出结果是一样的
//之前我用的char*,幸好第二天就被队友发现char*不能直接比较....赶紧改成string...感谢队友@henuzy
cout<<compare('b','a');//编译器自行脑补出的
}
根本不用定义数据类型!这样的话就只用写一个就行了,碰见其他的数据类型直接带进去就好了,大大提高了代码的重复利用性
是不是很赞??!!
例,对任意数据类型的输出,(举例子而已,直接用cout了....)
class Printer{//输出任意类型的数据
public:
template<typename T>
void print(const T &t){
cout<<t<<endl;
}
};
int main()
{
Printer p;
p.print<const char*>("abc");
p.print(1);
}
当模板函数的返回值类型需要用到另一个模板参数表示时,无法利用实参推断获取全部的类型参数,两种解决方法:
1.返回值类型与参数类型完全无关,那么就需要显示的指定返回值类型,其他的类型交给实参推断 此行为与函数的默认实参相同,我们必须从左向右逐一指定
给几个例子:
template<typename T1,typename T2,typename T3>
T1 sum(T2 v2,T3 v3){
return static_cast<T1>(v2+v3);
}
template<typename T1,typename T2,typename T3>
T3 sum_alternative(T1 v1,T2 v2){//返回T3型的函数,
return static_cast<T1>(v1+v2);//用到的是T1的数据
}
//2.返回值类型可以从参数类型中获得,那么把函数写成 尾置 返回类型的形式,就可以使用实参推断了
template<typename It>
auto sum(It beg,It end)->decltype(*beg){
decltype(*beg)ret=*beg;
for(It it=beg+1;it!=end;it++)
ret=ret+*it;
return ret;
}
int main()
{
//auto:自动推断变量的类型;
cout<<1L<<endl;//1L是int 的 1
auto long ret=sum<long>(1L,23);//指定T1,T2和T3交由编译器来推断
auto int ret=sum_alternative<long>(1L,23);//error,只能从左向右逐一指定
auto int ret=sum_alternative<long,int,long>(1L,23);//seccess,将最后一个T3作为返回类型
cout<<ret<<endl;
/*---------------------------------*/
vector<int>v={1,2,3,4};
auto s=sum(v.begin(),v.end());//s=10;
}
当然,有一些实参是会自己转换的,就三个:
实参推断时的自动转换类型
普通对象赋值给const引用:int a=0 - > const T&
数组名转换为头指针:int a[10]={0}; - > T*
函数名转换为函数指针:void func(int a){...} - > T*
函数模板的重载
函数模板之间,函数模板与普通函数之间可以重载。
普通函数(我们常用的函数)
特殊函数(限定了T的形式,指针,引用,容器等)
普通模板 (对T没有任何限制)
template<typename T>
void func(T& t){
cout<<"通用模板函数"<<t<<endl;
}
template<typename T>
void func(T* t){
cout<<"指针版本"<<*t<<endl;
}
void func(string *s)//仅仅是一个函数
{
cout<<"普通函数"<<*s<<endl;
}
//模板函数特化,定制函数,指定参数
template<>
void func(int i)
{
cout<<"int 的定制函数"<<i<<endl;
}
int main()
{
int i=10;
func(i);//调用普通版本,其它函数无法实例化||不匹配
func(&i);//调用指针版本,通用版本虽然可以用,但是编译器选择最特殊的版本
string s="abc";
func(&s);//调用普通函数,通用版本和特殊版本虽然可以用,但是编译器选择最特化的版本
func<>(&s);//调用指针版本,通过<>告诉编译器我们需要用template而不是普通函数
int res=10;
func(res);//调用特化版本的函数
}
目前我只会这么多,主要是想用class手动实现一下STL中的一些简单的数据结构;
以后再学再补吧。。