前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >函数模板 ## 函数模板

函数模板 ## 函数模板

原创
作者头像
Alan_1
发布2023-04-30 12:15:33
2.1K0
发布2023-04-30 12:15:33
举报
文章被收录于专栏:Alan的blogAlan的blog
  • 一般将模板放在头文件中

例子:

原型:

代码语言:c++
复制
template <typename AnyType>
void Swap(AnyType& a, AnyType& b);

实现:

代码语言:c++
复制
template <typename AnyType>        //typename可以用class代替,C++98提出使用关键字typename
void Swap(AnyType& a, AnyType& b)
{
	AnyType temp;
	temp = a;
	a = b;
	b = temp;
}
  1. 重载的模板
  • 需要多个对不同类型使用同一种算法的函数时,可使用模板。
  • 被重载的函数模板的特征标必须不同
  • 并非所有的模板参数都必须时模板参数类型
代码语言:c++
复制

//原型

template <typename T>

void Swap(T a, T b,int n);

//实现

template <typename T>

void Swap(T a[], T b[],int n)

{

代码语言:txt
复制
   T temp;
代码语言:txt
复制
   for(int i=0;i<n;i++)
代码语言:txt
复制
   {
代码语言:txt
复制
       temp=a[i];
代码语言:txt
复制
       a[i]=b[i];
代码语言:txt
复制
       b[i]=temp;
代码语言:txt
复制
   }

}

代码语言:txt
复制
  1. 模板的局限性

编写的函数模板可能无法处理某些类型

  1. 显式具体化

方法:

  • 对于给定的函数名,可以有⾮模板函数、模板函数和显式具体化模板函数以及它们的重载版本。
  • 显式具体化的原型定义应以template<>打头,并通过名称来指出类型。
  • 优先级:⾮模板函数>具体化>常规模板。
代码语言:c++
复制

//level 1:

void Swap(job& ,job&);

//level 2:

template <typename T>

void Swap(T&,T&);

//level 3:

template <>void Swap<job>(job&, job&); //or template <>void Swap(job&, job&);

代码语言:txt
复制
  1. 实例化和具体化
  • 记住,在代码 中包含函数模板本⾝并不会⽣成函数定义,它只是⼀个⽤于⽣成函数定 义的⽅案。
  • 编译器使⽤模板为特定类型⽣成函数定义时,得到的是模板实例(instantiation)。
  • 显式实例化语法:
代码语言:c++
复制

templat void Swap(int ,int);

代码语言:txt
复制
  • 同一个文件中使用同一种类型显式实例显式具体化将出错。
  • 隐式实例化、显式实例化和显式具体化统称为具体化。它们的相同之处在于,它们表⽰的都是使⽤具体类型的函数定义,⽽不是通⽤描述。
  • 引⼊显式实例化后,必须使⽤新的语法——在声明中使⽤前缀 templatetemplate <>,以区分显式实例化显式具体化
  1. 编译器选择使用哪个函数版本
  • 对于函数重载、函数模板和函数模板重载,C++需要(且有)⼀个 定义良好的策略,来决定为函数调⽤使⽤哪⼀个函数定义,尤其是有多 个参数时。这个过程称为重载解析(overloading resolution)。
  • 过程:
    1. 创建候选函数列表。其中包含与被调⽤函数的名称相同的函数和模板函数。
    2. 使⽤候选函数列表创建可⾏函数列表。这些都是参数数⽬正确的函数,为此有⼀个隐式转换序列,其中包括实参类型与相应 的形参类型完全匹配的情况。例如,使⽤float参数的函数调⽤可以 将该参数转换为double,从⽽与double形参匹配,⽽模板可以为 float⽣成⼀个实例。
    3. 确定是否有最佳的可⾏函数。如果有,则使⽤它,否则该函数调⽤出错。
  • 只考虑特征标,⽽不考虑返回类型。
  • 编译器必须确定哪个可⾏函数是最佳的。它查看为使函数调⽤参数与可⾏的候选函数的参数匹配所需要进⾏的转换。通常,从最 佳到最差的顺序如下所述。
    1. 完全匹配,但常规函数优先于模板。
    2. 提升转换(例如,char和shorts⾃动转换为int,float⾃动转换为 double)。
    3. 标准转换(例如,int转换为char,long转换为double)。
    4. ⽤⼾定义的转换,如类声明中定义的转换。
  1. 完全匹配和最佳匹配
代码语言:txt
复制
  Type(argument-list)意味着⽤作实参的函数名与⽤作形 参的函数指针只要返回类型和参数列表相同,就是匹配的。
代码语言:txt
复制
  ![image-20211108010839343](C:\Users\ALAN.XUAN\AppData\Roaming\Typora\typora-user-images\image-20211108010839343.png)
代码语言:txt
复制
  - 如果有多个匹配的原型,则编译器将⽆法完成重载解析过程;如果没有最佳的可⾏函数,则编译器将⽣成⼀条错误消息, 该消息可能会使⽤诸如“ambiguous(⼆义性)”这样的词语。
  - 有时候,即使两个函数都完全匹配,仍可完成重载解析。
    - 指向⾮const数据的指针和引⽤优先与⾮const指针和引⽤参数匹配。
    - const和⾮const之 间的区别只适⽤于指针和引⽤指向的数据。
    - ⼀个完全匹配优于另⼀个的另⼀种情况是,其中⼀个是⾮模板函 数,⽽另⼀个不是。在这种情况下,⾮模板函数将优先于模板函数(包 括显式具体化)。
    - 如果两个完全匹配的函数都是模板函数,则较具体的模板函数优 先。
    - 术语“最具体(most specialized)”并不⼀定意味着显式具体化,⽽ 是**指编译器推断使⽤哪种类型时执⾏的转换最少**。
  1. 部分排序规则示例
代码语言:txt
复制
  - ⽤于找出最具体的模板的规则被称为函数模板的部分排序规则
代码语言:txt
复制
  ```c++
代码语言:txt
复制
  //temptempover.cpp -- template overloading
代码语言:txt
复制
  #include<iostream>
代码语言:txt
复制
  template <typename T>
代码语言:txt
复制
  void ShowArray(T arr[], int n);
代码语言:txt
复制
  template <typename T>
代码语言:txt
复制
  void ShowArray(T* arr[], int n);
代码语言:txt
复制
  struct debts
代码语言:txt
复制
  {
代码语言:txt
复制
  	char name[50];
代码语言:txt
复制
  	double amount;
代码语言:txt
复制
  };
代码语言:txt
复制
  int main()
代码语言:txt
复制
  {
代码语言:txt
复制
  	using namespace std;
代码语言:txt
复制
  	int things[6] = { 13,31,103,301,310,130 };
代码语言:txt
复制
  	struct debts mr_E[3] =
代码语言:txt
复制
  	{
代码语言:txt
复制
  		{"jinlin",2400.0},
代码语言:txt
复制
  		{"alan",1300.0},
代码语言:txt
复制
  		{"xuan",1800.0}
代码语言:txt
复制
  	};
代码语言:txt
复制
  	double* pd[3];
代码语言:txt
复制
  	//设置指针pd指向mr_E的成员
代码语言:txt
复制
  	for (int i = 0; i < 3; i++)
代码语言:txt
复制
  	{
代码语言:txt
复制
  		pd[i] = &mr_E[i].amount;
代码语言:txt
复制
  	}
代码语言:txt
复制
  	cout << "Listing Mr. E's counts of things:\n";
代码语言:txt
复制
  	//things is an array of int
代码语言:txt
复制
  	ShowArray(things, 6);
代码语言:txt
复制
  	cout << "Listing Mr. E's debts:\n";
代码语言:txt
复制
  	//pd is an array of pointers to double
代码语言:txt
复制
  	ShowArray(pd, 3);
代码语言:txt
复制
  	return 0;
代码语言:txt
复制
  }
代码语言:txt
复制
  template <typename T>
代码语言:txt
复制
  void ShowArray(T arr[], int n)
代码语言:txt
复制
  {
代码语言:txt
复制
  	using namespace std;
代码语言:txt
复制
  	cout << "template A\n";
代码语言:txt
复制
  	for (int i = 0; i < n; i++)
代码语言:txt
复制
  	{
代码语言:txt
复制
  		cout << arr[i] << ' ';
代码语言:txt
复制
  	}
代码语言:txt
复制
  	cout << endl;
代码语言:txt
复制
  }
代码语言:txt
复制
  template <typename T>
代码语言:txt
复制
  void ShowArray(T* arr[], int n)
代码语言:txt
复制
  {
代码语言:txt
复制
  	using namespace std;
代码语言:txt
复制
  	cout << "template B\n";
代码语言:txt
复制
  	for (int i = 0; i < n; i++)
代码语言:txt
复制
  	{
代码语言:txt
复制
  		cout << *arr[i] << ' ';
代码语言:txt
复制
  	}
代码语言:txt
复制
  	cout << endl;
代码语言:txt
复制
  }
代码语言:txt
复制
  ```
代码语言:txt
复制
  输出:
代码语言:txt
复制
  ```c++
代码语言:txt
复制
  Listing Mr. E's counts of things:
代码语言:txt
复制
  template A
代码语言:txt
复制
  13 31 103 301 310 130
代码语言:txt
复制
  Listing Mr. E's debts:
代码语言:txt
复制
  template B
代码语言:txt
复制
  2400 1300 1800
代码语言:txt
复制
  ```
代码语言:txt
复制
  重载解析将寻找最匹配的函数。
代码语言:txt
复制
  - 如果只存在⼀个这样的 函数,则选择它;
  - 如果存在多个这样的函数,但其中只有⼀个是**⾮模板函数**,则选择该函数;
  - 如果存在多个适合的函数,且它们都为模板函 数,但其中有⼀个函数⽐其他函数**更具体**,则选择该函数。
  - 如果有多个 同样合适的⾮模板函数或模板函数,但没有⼀个函数⽐其他函数更具体,则函数调⽤将是不确定的,因此是错误的;
  1. 自己选择
代码语言:txt
复制
  在有些情况下,可通过编写合适的函数调⽤,引导编译器做出您希望的选择。
代码语言:txt
复制
  示例:
代码语言:txt
复制
  ```c++
代码语言:txt
复制
  //choices.cpp -- choosing a template
代码语言:txt
复制
  #include<iostream>
代码语言:txt
复制
  template <class T>       //#1
代码语言:txt
复制
  T lesser(T a, T b)
代码语言:txt
复制
  {
代码语言:txt
复制
  	return a < b ? a : b;
代码语言:txt
复制
  }
代码语言:txt
复制
  int lesser(int a, int b)	//#2
代码语言:txt
复制
  {
代码语言:txt
复制
  	a = a < 0 ? -a : a;
代码语言:txt
复制
  	b = b < 0 ? -b : b;
代码语言:txt
复制
  	return a < b ? a : b;
代码语言:txt
复制
  }
代码语言:txt
复制
  int main()
代码语言:txt
复制
  {
代码语言:txt
复制
  	using namespace std;
代码语言:txt
复制
  	int m = 20;
代码语言:txt
复制
  	int n = -30;
代码语言:txt
复制
  	double x = 15.5;
代码语言:txt
复制
  	double y = 25.9;
代码语言:txt
复制
  	cout << lesser(m, n) << endl;	//#2
代码语言:txt
复制
  	cout << lesser(x, y) << endl;	//#1 with double
代码语言:txt
复制
  	cout << lesser<>(m, n) << endl;		//#1 with int  
代码语言:txt
复制
      //要求进⾏显式实例化(使⽤int替代T),将使⽤显式实例化得到的函数。x和y的值将被强制转换为int,该函数返回⼀个int值,
代码语言:txt
复制
  	cout << lesser<int>(x, y) << endl;	//#1 with int	
代码语言:txt
复制
  	return 0;
代码语言:txt
复制
  }
代码语言:txt
复制
  ```
代码语言:txt
复制
  输出:
代码语言:txt
复制
  ```c++
代码语言:txt
复制
  20
代码语言:txt
复制
  15.5
代码语言:txt
复制
  -30
代码语言:txt
复制
  15
代码语言:txt
复制
  ```
代码语言:txt
复制
  - lesser<>(m, n)中的<>指出,编译器应选择模板函数,⽽不是⾮模板 函数;编译器注意到实参的类型为int,因此使⽤int替代T对模板进⾏实 例化。
  - 如果函数定义是在使⽤函数前提供的,它将充当函数原型。
  1. 多个参数的函数
代码语言:txt
复制
  编译器必须考虑所有参数的匹配情况

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档