二一、函数(四)

1、推断函数模板返回类型

1)引:将以下函数改写为一个函数模板

int ave(int a,int b)
{
    return (a+b)/2;
}
int ave(float a,fint b)
{
    return (a+b)/2;
}
double ave(int a,float b)
{
    return (a+b)/2;
}
//参数和返回值为2种数据类型的函数模板
template<typename T1,typename T2>  //模板定义时,可以定义多个类型
T1 ave(T2 a, T1 b)
{
	return (a + b) / 2;
}

int main()
{
	std::cout<<ave(100.55f, 200)<<std::endl;
	std::cout << ave<float,int>(100.55f, 200) << std::endl;  //可以强制性得指定模板中得参数类型
}
//参数和返回值为3种数据类型的函数模板
#include <iostream>
template<typename T1, typename T2,typename T3>  //模板定义时,可以定义多个类型
T3 ave(T2 a, T1 b)
{
	return (a + b) / 2;
}

int main()
{
	std::cout << ave<int,float,double>(100, 200.256f) << std::endl;  //可以强制性得指定模板中得参数类型和返回值类型
}

注:可以将函数模板的返回值,设置在第一个位置,当不知道参数类型的时候,只指定函数类型即可
#include <iostream>
template<typename TR, typename T2,typename T3>  //模板定义时,可以定义多个类型
TR ave(T2 a, T3 b)
{
	return (a + b) / 2;
}

int main()
{
	std::cout << ave<double>(100, 200.256f) << std::endl;  //可以强制性得指定模板中得参数类型和返回值类型
}
//如果传入的参数有一个可以确定参数,那么可以直接写明白参数的类型,不使用模板类型
#include <iostream>
template<typename TR, typename T2,typename T3>  //模板定义时,可以定义多个类型
TR ave(T2 a, T3 b,int c)  //第三个参数使用固定类型
{
	return (a + b) / 2;
}

int main()
{
	std::cout << ave<double>(100, 200.256f,100) << std::endl;  
}

2)decltype回返回引用类型

#include <iostream>
template<typename T1, typename T2>  //模板定义时,可以定义多个类型
auto bigger(T1 a, T2 b)   //使用auto推断函数的返回值,或者decltype(auto) bigger(T1 a, T2 b),auto
{
	return a>b?a:b;
}

int main()
{
	char a = 56;
	int b = 50000000;
	std::cout << bigger(a, b);
}

//使用decltype(auto)可以返回引用类型,可以修改函数值
#include <iostream>
template<typename T1, typename T2>  //模板定义时,可以定义多个类型
decltype(auto) bigger(T1 &a, T2 &b)   //使用auto推断函数的返回值,或者decltype(auto) bigger(T1 a, T2 b),auto
{
	return a>b?a:b;
}

int main()
{
	int a = 56;
	int b = 50000000;
	bigger(a, b) = -250;   //可以修改值
	std::cout << b << std::endl;
}

3)decltype不一定传回引用类型

#include <iostream>
template<typename T1, typename T2>  
decltype(auto) bigger(T1& a, T2& b)   
{
	return a > b ? a : b;  //类型不一样,会进行类型转化,类型转化以后,传出的值就不是一个引用类型了
}

int main()
{
	float a = 56;
	int b = 50000000;
	//float& lx = b;   //不是一个引用类型
	bigger(a, b);   //可以修改值
	std::cout << b << std::endl;
}
2、函数模板参数

1)函数模板默认参数(法一)

#include <iostream>
template<typename TR=int,typename T1, typename T2> //定义函数模板返回值的默认类型
TR ave(T1 a, T2 b)
{
	return (a+b)/2;  
}

int main()
{
	std::cout << ave(100, 200) << std::endl;       //使用默认的返回值类型
	std::cout << ave<float>(100, 200) << std::endl;  //使用指定的函数返回值类型
}

2)函数模板默认参数(法二)

//将函数模板返回值默认类型指定为参数类型
#include <iostream>
template<typename T1, typename T2,typename TR = T1>
TR ave(T1 a,T2 b)
{
	return (a + b) / 2;
}

int main()
{
	std::cout << ave(char(1), 200) << std::endl;  //输出d
}

3)非类型的模板参数

//非类型的函数模板参数
#include <iostream>
template<int max, int min,typename T>  //非类型的函数模板参数,此处的max和min不是变量
bool ChangeHp(T& hp,T damage)
{
	hp -= damage;
	if (hp > max)hp = max;
	//max=200;           //报错,因为max不是变量,是函数模板参数,是常量
	return hp < min;
}

int main()
{
	int hp = 2500;
	ChangeHp<2000,1000>(hp, 100);  //需要指定非类型的函数模板参数的值
	std::cout << hp << std::endl; //输出2000,因为最大值为2000
}
//可以在函数模板定义时,就指定函数模板参数
//非类型的函数模板参数
#include <iostream>
template<int max=2000, int min=1000, typename T>  //模板定义时,直接指定非类型的函数模板参数的值
bool ChangeHp(T& hp, T damage)
{
	hp -= damage;
	if (hp > max)hp = max;
	//max=200;           //报错,因为max不是变量,是函数模板参数,是常量
	return hp < min;
}

int main()
{
	int hp = 2500;
	ChangeHp(hp, 100);  
	std::cout << hp << std::endl; 
}

3)非类型模板参数实现处理固定大小的数组

//计算数组中元素的和
//处理不同元素个数的数组
#include <iostream>

template<typename T,short count>
T ave(T(&ary)[count])                 //并没有强制设置count的值
{
	T all{};
	for (int i = 0; i < count; i++)
		all += ary[i];
	return all / count;
}

int main()
{
	int a[5]{ 1,2,3,4,5 };
	std::cout << ave(a) << std::endl;
}
3、函数模板的本质

编译器根据函数模板给定的参数,生成多个不同类型的函数

//C++
#include <iostream>
template <typename T>
T ave(T a, T b)
{
	return a + b;
}

int main()
{
	ave(100, 200);
	ave(short(100), short(200));
}

//函数调用汇编
ave(100, 200);
000B1991 68 C8 00 00 00       push        0C8h   //200
000B1996 6A 64                push        64h    //100
000B1998 E8 48 F8 FF FF       call        ave<int> (0B11E5h)   //函数调用
000B199D 83 C4 08             add         esp,8  
	ave(short(100), short(200));
000B19A0 68 C8 00 00 00       push        0C8h  
000B19A5 6A 64                push        64h  
000B19A7 E8 06 F9 FF FF       call        ave<short> (0B12B2h)  //函数调用
000B19AC 83 C4 08             add         esp,8  
    
//注:可以发现,调用同一个函数,调用的内存地址不一样,说明编译器根据函数模板,自动生成了两个ave()的函数
4、练习:排序工具
//需求:利用函数模板实现以下函数:能够给数组内的元素自动排序
int a[5]{2302,5212,3654,9740,5200};
Sort(a,5);

short a[5]{2302,5212,3654,9740,5200};
Sort(a,5);

std::string a[5]{"abc","bcd","cde","fgh","ijk"}
Sort(a,5)
//不使用函数模板
#include <iostream>

void Swap(int& a,int& b)
{
	int tmp{ a };
	a = b;
	b = tmp;
}

void Sort(int* ary,unsigned count,bool Bigsort = true)
{
	for(int i=1;i<count;i++)
		for (int i = 1; i < count; i++)
		{
			bool bcase = Bigsort ? ary[i] > ary[i - 1]:ary[i] < ary[i - 1];
			if (bcase) Swap(ary[i], ary[i - 1]);
		}
}
int main()
{
	int a[5]{ 2302,5212,3654,9740,5200 };
	Sort(a, 5);
	for (auto x : a)std::cout << x << std::endl;
}
//使用函数模板实现上述功能
#include <iostream>

template<typename T>
void Swap(T& a, T& b)
{
	T tmp{ a };
	a = b;
	b = tmp;
}
template<typename T>
void Sort(T* ary, unsigned count, bool Bigsort = true)
{
	for (T i = 1; i < count; i++)
		for (T i = 1; i < count; i++)
		{
			bool bcase = Bigsort ? ary[i] > ary[i - 1]:ary[i] < ary[i - 1];
			if (bcase) Swap(ary[i], ary[i - 1]);
		}
}
int main()
{
	int a[5]{ 2302,5212,3654,9740,5200 };
	short b[5]{ 2302,5212,3654,9740,5200 };
	Sort(a, 5);
	for (auto x : a)std::cout << x << std::endl;
	std::cout << "+++++++++++++++++++++++++++++" << std::endl;
	Sort(b, 5);
	for (auto x : a)std::cout << x << std::endl;
}