template的使用,泛式编程

一个偶然的机会,让我接触到了泛式编程,它的美妙简直让我沉迷其中,短时间的学习,让我喜欢上了这个方法,它的出现让我想到了之前许多不理解的地方,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中的一些简单的数据结构;

以后再学再补吧。。

猜你喜欢

转载自blog.csdn.net/qq_40482358/article/details/82501243
今日推荐