函数模板,模板形参的推演

1//函数模板的泛化应用/

函数模板,泛型编程思想,代码复用性强,使用方便。

查看如下代码:


template<class T> //typename
T compare(T x, T y)
{
    return x+y;
}
int main()
{
    int x=10,y=20;
    string s1 = "acf";
    string s2 = "ved";
    auto res = compare(x,y);
    auto res1 = compare(s1,s2);
    cout<<res<<endl;
    cout<<res1<<endl;
    return 0;
}
#endif

输出:

30
acfved

2. /部分特化:编译时确定数组的个数/

#include<cstring>
template<unsigned N, unsigned M>
int compare(const char(&p1)[N],const char(&p2)[M])
{
    return strcmp(p1,p2);
}
int main()
{
    string a="123";
    string c ="bac";
    int res = compare("aaha","aaha"); 
    cout<<res<<endl;
    return 0;
}

完全特化的一个例子:

template<class T>
void fun(T a){cout<<"普通函数模板"<<endl;}
template<>
void fun<const char *>(const char *a){
    cout<<"完全特化"<<a<<endl;
}
int main()
{
    int a=10;
    const char ar[] = "yhpign";
    //fun(a);
    fun(&a);
    fun<const char *>(ar);此加尖括号显示表示执行完全特化中const char * 类型函数
    fun(ar);不加尖括号会随机执行类型兼容的函数体
   
    return 0;
}

输出:
普通函数模板
完全特化yhpign
完全特化yhpign

完全特化注意:1.必须有一个主模板类 2.模板类型被全部明确化


3.函数模板几个灵活使用的例子

/*函数模板的几种灵活使用方式*/
template<class T,class N >
T Max(T x, N y){
    cout<<"普通函数模板"<<endl;
    return x>y;
}
 template<class T>
 int Max(int x,T y){
    cout<< "部分特化"<<endl;
     return x>y;
 }
  template<class T,class N>
 int Max(T* x,N* y){
    cout<< "形参以指针方式使用 "<<endl;
     return *x>*y;
 }
 template<class T,class N>
 int Max(vector<T>&left,vector<N>&right)
 {
     cout<< "形参确定了一个参数是模板类类型"<<endl;
     return left.size()>right.size();
 }
int main()
{
    int x =10,y = 20;
    //1 ,int res = Max(x,y); //隐式调动,若不显示指定参数类型,则在上述函数模板中可能不会 调用你想调动的函数
    int res = Max<int,int>(x,y);//显示调动,确定调动的函数模板
    //cout<<res<<endl;
    const char *ps = "1";
    //2,
    int res1 = Max(x,'1');
    //3,
    int res2 = Max(&x,ps);
    vector<int> vecLeft{0};
	vector<char> vecRight{'1','2','3'};
    //4,
    int  res3 = Max(vecLeft,vecRight);
    return 0;
}

输出:
普通函数模板
部分特化
形参以指针方式使用
形参确定了一个参数是模板类类型

部分特化注意:1.必须有一个主模板类 2.模板类型被部分明确化


例子1:我们简单的实现大小的比较的泛型编程:

class Int{
int val_;
public:
    Int(int val):val_(val){}
    ~Int(){}
    operator int()const //没const编译不通过//括号后加const啥用?
    //作用1.类成员不可发生改变
    //作用2.类是const 类时,类的成员函数后要加const (常性)否则编译不通过。
    {
        return val_;
    }
};
template<class  T>
const T  Min(const T&x,const T&y)  //此时Int类的a是const Int& 类型
{                                  //,调强转函数,后面肯定要加const才能通过编译                        
    return x > y ? y:x; 
}
int main()
{
	Int a(3),b(4);
	int res = Min(a, b);
	cout << res << endl;
	return 0;
}

例子2:以上看似没什么问题,但是要让字符串比较,就会出现问题,之前比较函数比较的是地址的大小。我们需要在此基础上写一个部分特化


template<class  T>
const T&  Min(const T&x,const T&y)
{
    return  x>y?y:x;
}
template<>
  const char  * const &   Min<const char *>(const char *const &p1,const char *const &p2)
{
    return strcmp(p1,p2)>0?p2:p1;
}
int main()
{
    const char *p1 = "lmy";
    const char *p2 = "yml";
    int val = Min(1,3);
    auto res = Min<const char *>(p1,p2);
    cout<<res<<endl;
	return 0;
}

这样就实现了一个比较的特化版本,还是比较方便的。
需要注意的是:他看起来是函数的重载,有什么区别?

程序运行结果和使用函数模板特化相同。但是,使用普通函数重载和使用模板特化还是有不同之处,主要表现在如下两个方面:

(1)如果使用普通重载函数,那么不管是否发生实际的函数调用,都会在目标文件中生成该函数的二进制代码。而如果使用模板的特化版本,除非发生函数调用,否则不会在目标文件中包含特化模板函数的二进制代码。这符合函数模板的“惰性实例化”准则。

(2)如果使用普通重载函数,那么在分离编译模式下,需要在各个源文件中包含重载函数的申明,否则在某些源文件中就会使用模板函数,而不是重载函数。


4.非模板的形参推演

//在非模板中叫做万能引用:const int &
void fun(const int &x){cout<<"const int&"<<endl;}
//void fun(int x){cout<<" int"<<endl;}
void fun(int &x){cout<<" int&"<<endl;}
void fun(int &&x){cout<<"int &&"<<endl;}
int main()
{   
    int x =10;
    const int y = 20;
    int *p = &x;
    fun(x); //1:int &,2:const int &
    fun(y);//1:const int & 
    fun(20); //1:int && ,2:const int &
    return 0;
}

引用类型未定义:

/*形参是右值引用,属于引用类型未定义*/
template <class T>
void func(T&& d) {
	T y; //对于func(a)和func(b)来说报错,因为T 是引用类型。
    T y = 10;//对于func(a)来说报错 
} 

int main()
{
	int a = 10;
	const int b = 20;
	func(a); //T :int &, d: int & ;传入一个左值,这两个类型都是左值引用;
	func(b);// T :const int &,const int &//T y=10;传入一个常性左值,这两个类型都是常性左值引用
	func(20);//T int //d 是int&& 
}

完美转发

void print(int &a){cout<<"int &"<<endl;}
void print(const int &a){cout<<"const int &"<<endl;}
void print(int &&a){cout<<"int &&"<<endl;}  
template <class T>
void func(T&& d) {
	print(std::forward<T>(d)); //完美转发 ,此时让右值保持右值的属性
} 

int main()
{
	int a = 10;
	const int b = 20;
	func(a); //T :int &, d: int & ;传入一个左值,这两个类型都是左值引用;
	func(b);// T :const int &,const int &//T y=10;传入一个常性左值,这两个类型都是常性左值引用
	func(20);//T int //d 是int&& 
}

5.模板形参的推演

形参以值接收:

template<class T>
void func(T x) { //形参以值接收 **
}
int main()
{
	int a = 10;
	const int b = 20;
	int& ra = a;
	const int rb = b;
	func(a);//T:int
	func(b);//T:int 
	func(ra);//T:int
	func(rb);//T:const int
	func(&a);//T:int * 传指针,会改变实参的值,T为int *
	func(&b);//T:const int *//不能改变b的值,加const 
	return 0;
}

形参以引用接收:

#if 0
template<class T>
void func(T &x) { //以引用接收的类型推演**
	T y = 10;
}
int main()
{
	int a = 10;
	const int b = 20;
	int& ra = a;
	const int rb = b;
	//func(a); //x:int &//T :int
	func(b);//x:const int &//T :const int
	//func(ra);//x:int &  //T: int
	//func(rb);//x:const int&//T:const int
	//func(&a);//error  函数以引用接受,传指针报错
	//func(&b);
	return 0;
}

//  template<class T>
// void fun(   T x){} //a = 10;fun(a)->即调fun(T &x) 也调fun(T x) 调用不明确
template<class T>
void fun(T &x){cout<<"T &"<<endl;}
template<class T>
void fun(T &&x){cout<<"T &&"<<endl;}
template<class T>
void fun(const T&){cout<<"const T &"<<endl;}

int main()
{
	int a = 10;
	const int b = 20;
	fun(a);
    fun(b);
    fun(20);
    return 0;
}

输出:
T &
const T &
T &&

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值