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