实例代码:
//
#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");
}