函数模板是泛型函数描述;也就是说,它根据泛型类型定义了一个函数,特定类型(如int或double)可以被替换。通过将类型作为参数传递给模板,可以使编译器为该特定类型生成一个函数。因为模板允许您根据泛型类型而不是特定类型编程,所以这个过程有时被称为泛型编程。因为类型是由参数表示的,所以模板特性有时被称为参数化类型。
//理论上来说typename和class都是可以的,但是考虑到向后兼容性和敲出更短的词汇,建议使用class
template <typename AnyType>
void Swap(AnyType &a, AnyType &b)
{
AnyType temp;
temp = a;
a = b;
b = temp;
}
template <class AnyType>
void Swap(AnyType &a, AnyType &b)
{
AnyType temp;
temp = a;
a = b;
b = temp;
}
// funtemp.cpp -- using a function template
#include
// function template prototype
template <typename T> // or class T
void Swap(T &a, T &b);
int main()
{
using namespace std;
int i = 10;
int j = 20;
cout << "i, j = " << i << ", " << j << ".\n";
cout << "Using compiler-generated int swapper:\n";
Swap(i,j); // generates void Swap(int &, int &)
cout << "Now i, j = " << i << ", " << j << ".\n";
double x = 24.5;
double y = 81.7;
cout << "x, y = " << x << ", " << y << ".\n";
cout << "Using compiler-generated double swapper:\n";
Swap(x,y); // generates void Swap(double &, double &)
cout << "Now x, y = " << x << ", " << y << ".\n";
// cin.get();
return 0;
}
// function template definition
template <typename T> // or class T
void Swap(T &a, T &b)
{
T temp; // temp a variable of type T
temp = a;
a = b;
b = temp;
}
当函数调用到下面这句时,系统自动生成函数void Swap(int &a, int &b),将其存储在函数的位置,你不会看到它,但是系统会自动生成。
Swap(i,j); // generates void Swap(int &, int &)
i, j = 10, 20.
Using compiler-generated int swapper:
Now i, j = 20, 10.
x, y = 24.5, 81.7.
Using compiler-generated double swapper:
Now x, y = 81.7, 24.5.
一般情况下函数模板放到头文件中。
算法不同且参数个数or种类(数组、结构体、class)不同时,由于算法不同,单靠模板就无法解决全部问题,因此需要函数模板重载。
举例:
// twotemps.cpp -- using overloaded template functions
#include
template <typename T> // original template
void Swap(T &a, T &b);
template <typename T> // new template
void Swap(T *a, T *b, int n);
void Show(int a[]);
const int Lim = 8;
int main()
{
using namespace std;
int i = 10, j = 20;
cout << "i, j = " << i << ", " << j << ".\n";
cout << "Using compiler-generated int swapper:\n";
Swap(i,j); // matches original template
cout << "Now i, j = " << i << ", " << j << ".\n";
int d1[Lim] = {0,7,0,4,1,7,7,6};
int d2[Lim] = {0,7,2,0,1,9,6,9};
cout << "Original arrays:\n";
Show(d1);
Show(d2);
Swap(d1,d2,Lim); // matches new template
cout << "Swapped arrays:\n";
Show(d1);
Show(d2);
// cin.get();
return 0;
}
template <typename T>
void Swap(T &a, T &b)
{
T temp;
temp = a;
a = b;
b = temp;
}
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;
}
}
void Show(int a[])
{
using namespace std;
cout << a[0] << a[1] << "/";
cout << a[2] << a[3] << "/";
for (int i = 4; i < Lim; i++)
cout << a[i];
cout << endl;
}
i, j = 10, 20.
Using compiler-generated int swapper:
Now i, j = 20, 10.
Original arrays:
07/04/1776
07/20/1969
Swapped arrays:
07/20/1969
07/04/1776
对于这个模板
template <class T> // or template
void f(T a, T b)
{...}
如果a和b是数组或结构体或class,那么下面的语句是不允许的:
a = b;
if (a > b)
T c = a*b;
在这种情况下就要重载操作符才可以实现,这个内容会在后面介绍到。
算法不同且参数个数种类相同
假设定义了如下的结构体:
struct job
{
char name[40];
double salary;
int floor;
};
假设定义了job的a,b变量,想要交换两者的salary和floor但不交换name,由于其参数数据类型和下面这个函数一致,所以无法使用重载函数模板解决问题,而要寻找别的办法—他就叫显式具体化。
template <typename T>
void Swap(T &a, T &b)
{
T temp;
temp = a;
a = b;
b = temp;
}
1.对于给定的函数名,可以有非模板函数、模板函数和显式具体化模板函数以及他们的重载版本。
2.显式具体化的原型和定义应以template<>打头,并通过名称来指出类型。
3.具体化优先于常规函数,非模板函数优先于具体化和常规函数。
//这两个都行
template <> void Swap(job &, job &); // simpler form
template <> void Swap<job>(job &j1, job &j2);
// twoswap.cpp -- specialization overrides a template
#include
template <typename T>
void Swap(T &a, T &b);
struct job
{
char name[40];
double salary;
int floor;
};
// explicit specialization
template <> void Swap<job>(job &j1, job &j2);
void Show(job &j);
int main()
{
using namespace std;
cout.precision(2);
cout.setf(ios::fixed, ios::floatfield);
int i = 10, j = 20;
cout << "i, j = " << i << ", " << j << ".\n";
cout << "Using compiler-generated int swapper:\n";
Swap(i,j); // generates void Swap(int &, int &)
cout << "Now i, j = " << i << ", " << j << ".\n";
job sue = {"Susan Yaffee", 73000.60, 7};
job sidney = {"Sidney Taffee", 78060.72, 9};
cout << "Before job swapping:\n";
Show(sue);
Show(sidney);
Swap(sue, sidney); // uses void Swap(job &, job &)
cout << "After job swapping:\n";
Show(sue);
Show(sidney);
// cin.get();
return 0;
}
template <typename T>
void Swap(T &a, T &b) // general version
{
T temp;
temp = a;
a = b;
b = temp;
}
// swaps just the salary and floor fields of a job structure
template <> void Swap<job>(job &j1, job &j2) // specialization
{
double t1;
int t2;
t1 = j1.salary;
j1.salary = j2.salary;
j2.salary = t1;
t2 = j1.floor;
j1.floor = j2.floor;
j2.floor = t2;
}
void Show(job &j)
{
using namespace std;
cout << j.name << ": $" << j.salary
<< " on floor " << j.floor << endl;
}
i, j = 10, 20.
Using compiler-generated int swapper:
Now i, j = 20, 10.
Before job swapping:
Susan Yaffee: $73000.60 on floor 7
Sidney Taffee: $78060.72 on floor 9
After job swapping:
Susan Yaffee: $78060.72 on floor 9
Sidney Taffee: $73000.60 on floor 7
编译器使用模板为特定类型生成函数定义时,得到的是函数实例。
函数调用Swap(i,j) 导致编译器使用int类型生成一个Swap()实例。模板不是函数定义,但是使用int类型的模板实例是函数定义。
程序根据数据类型推断要生成哪种函数定义,所以叫隐式实例化。
命令编译器创建特定的实例化。
template void Swap<int>(int, int); // explicit instantiation
//Use the Swap() template to generate a function definition for the int type.
比较与显式具体化的不同在于省略了<>:显式具体化在警告编译器不要使用函数模板为生成函数定义,取而代之的是,使用专门为int类型显式地定义地函数定义。
template <> void Swap<int>(int &, int &); // explicit specialization
template <> void Swap(int &, int &); // explicit specialization
注意事项:试图在同一个文件中使用同一种类型地显式实例化和显式具体化将出错。
在程序中使用函数来创建显式实例化:
template <class T>
T Add(T a, T b) // pass by value
{
return a + b;
}
...
int m = 6;
double x = 10.2;
cout << Add<double>(x, m) << endl; // explicit instantiation
这个模板与Add(x, m)不匹配,因为该模板要求两个函数参数的类型相同。但通过使用Add(x, m),可强制为double类型实例化,并将参数m强制转换为double类型,以便与函数Add(double, double) 的第二个参数匹配。
显式实例化有什么作用:就是编辑时可检查,出现错误能够及时修改。
...
template <class T>
void Swap (T &, T &); // template prototype
template <> void Swap<job>(job &, job &); // explicit specialization for job
int main(void)
{
template void Swap<char>(char &, char &); // explicit instantiation for char
short a, b;
...
Swap(a,b); // implicit template instantiation for short
job n, m;
...
Swap(n, m); // use explicit specialization for job
char g, h;
...
Swap(g, h); // use explicit template instantiation for char
...
}
...
Eg:为下面这个函数调用匹配函数:
may('B'); // actual argument is type char
可选项有下面这些:
void may(int); // #1
float may(float, float = 3); // #2
void may(char); // #3
char * may(const char *); // #4
char may(const char &); // #5
template<class T> void may(const T &); // #6
template<class T> void may(T *); // #7
首先是要排除4和7的,因为类型不匹配。
其他的优先级从好到坏的排序按以下规则:
Function #1比Function #2要好因为char-to-int is a promotion,但是char-to-float is a standard conversion.Functions #3, #5, and #6比Functions #1 or #2要好,因为Functions #3, #5, and #6是绝对匹配的。Both #3 and #5 are better than #6 because #6 is a template.
通常有两个完全匹配是一种错误,目前函数3,5无法确定谁胜出,因此要更深入讨论。
Type (argument-list)表示实参的函数名与用作形参的函数指针只要返回类型和参数列表相同,就是匹配的。
//针对这个:
struct blot {int a; char b[10];};
blot ink = {25, "spots"};
...
recycle(ink);
//这些都是exact matches
void recycle(blot); // #1 blot-to-blot
void recycle(const blot); // #2 blot-to-(const blot)
void recycle(blot &); // #3 blot-to-(blot &)
void recycle(const blot &); // #4 blot-to-(const blot &)
struct blot {int a; char b[10];};
template <class Type> void recycle (Type t); // template
template <> void recycle<blot> (blot & t); // specialization for blot
...
blot ink = {25, "spots"};
...
recycle(ink); // use specialization
术语最具体化并不一定意味着明确的具体化;更普遍的是,它表明,当编译器推断出什么类型时,发生的转换更少。
template <class Type> void recycle (Type t); // #1
template <class Type> void recycle (Type * t); // #2
对于
struct blot {int a; char b[10];};
blot ink = {25, "spots"};
...
recycle(&ink); // address of a structure
函数1(解释为recycle
// tempover.cpp -- template overloading
#include
template <typename T> // template A
void ShowArray(T arr[], int n);
template <typename T> // template B
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] =
{
{"Ima Wolfe", 2400.0},
{"Ura Foxe", 1300.0},
{"Iby Stout", 1800.0}
};
double * pd[3];
// set pointers to the amount members of the structures in 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); // uses template A
cout << "Listing Mr. E's debts:\n";
// pd is an array of pointers to double
ShowArray(pd, 3); // uses template B (more specialized)
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
template<class T> // or template
T lesser(T a, T b) // #1
{
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; // use #2
cout << lesser(x, y) << endl; // use #1 with double
cout << lesser<>(m, n) << endl; // use #1 with int
cout << lesser<int>(x, y) << endl; // use #1 with int
return 0;
}
20
15.5
-30
15
D:\Prj\C++\Template_Makeing_Your_Own_Choice\Debug\Template_Makeing_Your_Own_Choice.exe (进程 9904)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .