C++语法学习笔记五十五:模板概念,函数模板定义、调用

实例代码:

// 

#include <iostream>
#include <vector>

using namespace std;

int funcadd(int i1, int i2){
	int addhe = i1 + i2;
	return addhe;
}

double funcadd(double d1, double d2){
	double addhe = d1 + d2;
	return addhe;
}

//函数模板 (公式/样板)
template<typename T>  //因为T前边有一个 typename/class ,这表示T代表一个类型,是一个类型参数。
T funcadd(T a, T b){
	T addhe = a + b;
	return addhe;
}

//编译器给我们用模板函数funcadd实例化出来int 类型版本
//int funcadd(int a, int b){
//	int addhe = a + b;
//	return addhe;
//}


//编译器给我们用模板函数funcadd实例化出来double 类型版本
//double funcadd(double a, double b){
//	double addhe = a + b;
//	return addhe;
//}

template<int a, int b>
int funcaddv2()
{
	int addhe = a + b;
	return addhe;
}

template<typename T, int a, int b>
int funcaddv3(T c)
{
	int addhe = (int)c + a + b;
	return addhe;
}


template<unsigned L1, unsigned L2>  //本例中仍旧没有类型参数。只有非类型模板参数
int charscomp(const char(&p1)[L1], const char(&p2)[L2])
{
	return strcmp(p1, p2);
}





int main()
{
	//一: 概述
	// vector   :vector<int>

	//几句话:
	//(1)所谓泛型编程 是以独立于任何特定类型的方式编写代码。使用泛型编程时,我们需要提供具体程序实例所操作的类习惯或者值;
	//(2)模板是泛型编程的基础。模板是创建类或者函数的蓝图或者公式。我们给这些蓝图或者公式提供足够的信息,让这些蓝图或者公式真正的转变具体的类或者函数,这种转变发生在编译时。
	//(3)模板支持 将 类型作为参数的程序设计方法,从而实现了对泛型程序设计的直接支持。也就是说,C++模板机制允许在定义类、函数时将类型作为参数。


	//二:函数模板的定义
	//(1) 模板定义是用 template关键字开头的,后边跟<>, <>里边叫模板参数列表(模板实参),如果模板参数列表里有多个参数,则用逗号分开。
	//<> 里必须至少得有一个模板参数,模板参数前面有个typename/class (不是用来定义类的)关键字。
	//template<typename T> 这种写法大家硬记。
	//如果模板参数列表里边有多个模板参数,那你就要多个typename/class: <typename T, typename Q>

	//(2) 模板参数列表里边表示在函数定义中用到的 “类型” 或者 “值”,也和函数参数列表类似。
	//那我们用的时候,有的时候得指定 模板实参给他,指定的时候我们要用<> 把模板实参包起来。有的时候 又不需要我们指定模板实参给他,
	//系统自己能够根据一些信息推断出来,后续我们都会举例;

	//(3) funcadd这个函数声明了一个名字为T的类型参数。这里注意,T实际是类型,这个T到底代表的是啥类型,编译器在编译的时候会根据针对funadd()的调用来确定。


	//三:函数模板的使用
	//函数模板调用和函数调用区别不大, 调用的时候,编译器会根据你调用这个函数模板时的实参 去 推荐 模板参数列表里的参数(形参)的类型,所以
	//大家在这里一定要注意措辞:模板参数 有时候 是推断出来的(有的时候是需要你提供),推断的依据是什么?是根据你调用这个函数的实参来推断的;
	//当然有的时候,光凭借函数实参是推断不出来模板参数的,这个时候我们就得用<> 来主动的提供模板参数了;

	//template<typename T>
	//T funcadd(T a, T b);

	int he = funcadd(3, 1); // 3,1 系统认为是int,所以编译器能推断出来模板的形参是个int型。也就说那个参数T是int型;
	cout << he << endl;

	//编译器在推断出来这个模板的形参类型之后,编译器就为我们实例化了一个特定版本的函数。
	double he2 = funcadd(3.1f, 1.2f);
	cout << he2 << endl;

	//double he3 = funcadd(3, 1.2f); //报错,系统不知道该推断成int 还是double

	//四:非类型模板参数
	//因为T前边有一个typename/class,这表示T代表一个类型,是一个类型参数。
	//那么在模板参数列表里边,还可以定义非类型参数;非类型参数代表的是一个值。
	//既然非类型参数代表一个值,那么我们肯定不能用typename/class这种关键字来修饰这个值。
	//我们当然要用以往学习过的传统类型名来指定非类型参数了,比如你非类型参数S如果是个整形,int s。

	//template<typename T, int S>
	//T funcadd(T a, T b)

	//当模板被实例化时,这种非类型模板参数的值 或者 是用户提供的,或者是编译器推断的,都有可能。
	//但是,这些值必须都得是常量表达式,因为实例化这些模板是编译器在编译的时候来实例化的;

	//template<T a, T b>
	//int funcaddv2();

	int result = funcaddv2<12, 13>(); //显式的指定模板参数 -- 在尖括号中提供额外的信息。
	cout << result << endl;


	int a = 12;
	//result = funcaddv2<a, 13>(); //这不可以:非类型模板参数 值必须是在编译的时候就能够确定,因为实例化函数模板是在编译的时候干的事。


	//template<typename T, int a, int b>
	//int funcaddv3(T c)

	result = funcaddv3<int, 11,12>(13);
	cout << result << endl;

	result = funcaddv3<double, 11, 12>(13); //系统会 以我们用 <> 传递进去的类型为准,而不是用13来推断什么类型。
	cout << result << endl;

	//template<unsigned L1, unsigned L2>  //本例中仍旧没有类型参数。只有非类型模板参数
	//int charscomp(const char(&p1)[L1], const char(&p2)[L2])

	result = charscomp("test2", "test"); //没有提供非类型模板参数,系统会根据test2的长度6个,test长度5个,取代 L1,L2;
	cout << result << endl;

	//模板定义并不会导致编译器生成代码。只有在我们调用这个函数模板时,使编译器为我们实例化了一个特定版本的函数之后,编译器才会生成代码。

	//编译器生成代码的时候,需要能够找到函数的函数体,所以,函数模板的定义通常都是在.h 文件中。


	system("pause");
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值