part 1
函数模板(p281)是通用的函数描述,它们使用泛型来定义函数,其中的泛型可用具体的类型(如int/double)替换。通过将类型作为参数传递给模板,可使编译器生成该类型的函数。
可以建立这样一个交换模板:
template <typename T>
void Swap(T &a,T &b)
{
T temp;
temp = a;
a = b;
b = temp;
}
Note:1、建立一个模板,并将类型名命名为T;关键字typename可用class代替;必须使用尖括号,没有分号。
2、声明和定义时不能只写void Swap(T &a,T &b)还要加上template语句
3、模板并不创建任何函数,而只是告诉编译器如何定义函数。需要交换int的函数时,编译器将按模板模式创建这样的函数,并用int代替T。
#include<iostream>
template <typename T>
void Swap(T &a,T &b);
int main()
{
char a = 'o',b = 'k';
Swap(a,b);
std::cout << a << std::endl << b;
}
template <typename T>
void Swap(T &a,T &b)
{
T temp;
temp = a;
a = b;
b = temp;
}
最终的代码不包含任何模板,而只包含了为程序生成的实际函数。使用模板的好处是,它使生成多个函数定义更简单、更可靠。
part 2
重载的模板
可以像重载常规函数定义那样重载模板定义,被重载的模板的函数特征标必须不同。
#include<iostream>
template <typename T>
void Swap(T &a,T &b);
template <typename T>
void Swap(T a[],T b[],int n);
template<typename T>
void show(T a[],int n = 5);
int main()
{
char a = 'o',b = 'k';
Swap(a,b);
std::cout << a << std::endl << b << std::endl;
int arr1[10] = {1,2,3,4,5,6,7,8,9,10};
int arr2[10] = {10,9,8,7,6,5,4,3,2,1};
Swap(arr1,arr2,10);
show(arr1,10);
show(arr2,10);
char s1[] = "cqupt";
char s2[] = "xupts";
Swap(s1,s2,5);
show(s1,5);
show(s2,5);
}
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;
}
}
template<typename T>
void show(T a[],int n )
{
using namespace std;
for(int i = 0;i < n;i++)
cout << a[i] << ",";
std::cout << std::endl;
}
part3
模板的局限性:编写的模板可能无法处理某些类型。
显式具体化:假设定义了如下结构。
struct job
{
/* data */
char name[40];
double salary;
int floor;
};
c++运行将一个结构赋给另一个结构,我们只想交换salary和floor成员,而不交换name成员。
然而,可以提供一个具体化函数定义——称为显示具体化,其中包含所需的代码。当编译器找到与函数调用匹配的具体化定义时,将使用该定义,而不再寻找模板。
第三代具体化(ISO/ANSI C++标准)
对于给定的函数名,可以有非模板函数、模板函数、显式具体化模板函数以及它们的重载版本。
显示具体化的原型和定义应以template<>打头,并通过名称来指出类型。
具体化优于常规函数,而非模板函数优于具体化和常规函数。
template<>void Swap<job>(job &j1,job &j2);
其中<job>是可选的,也就是可以去掉。
示例:
#include<iostream>
struct job
{
/* data */
char name[40];
double salary;
int floor;
};
template <typename T>
void Swap(T &a,T &b);
template<>void Swap<job>(job &j1,job &j2);
void show(job &a);
int main()
{
job staff1 = {"jack",2333.3,6};
job staff2 = {"vic",9000,9};
Swap(staff1,staff2);
show(staff1);
show(staff2);
}
template <typename T>
void Swap(T &a,T &b)
{
T temp;
temp = a;
a = b;
b = temp;
}
template<>void Swap<job>(job &j1,job &j2)
{
double s;
int f;
s = j1.salary;
j1.salary = j2.salary;
j2.salary = s;
f = j1.floor;
j1.floor = j2.floor;
j2.floor = f;
}
void show(job &a)
{
std::cout << a.name << ": " << a.salary
<< " RMB " << a.floor << "floor(s)\n";
}
part4
实例化和具体化
记住,代码中包含函数模板本身并不会生成函数定义,他只是一个用于生成函数定义的方案。
上述代码中Swap(i,j);导致编译器生成Swap()的一个实例,该实例使用int类型。模板并非函数定义,但使用int的模板实例是函数定义,这种实例化方法被称为隐式实例化。
C++还允许显式实例化,这意味着可以直接命令编译器创建特定的实例,其语法是,声明所需的种类——用<>符号指示类型,并在声明前加上关键字template:
template void Swap<int>(int,int);
该声明的意思是”使用Swap()模板生成int类型的函数定义“
与显式实例化不同,显式具体化使用下面两个等价的声明之一
template<>void Swap<int>(int &,int &);
template<>void Swap(int &,int &);
这些声明的意思是”不要使用Swap()模板来生成函数定义,而应使用专门为int类型显式地定义的函数定义。“这些原型必须有自己的函数定义。
隐式实例化、显示实例化和显示具体化统称为具体化。它们的相同之处在于,它们表示的都是使用具体类型的函数定义,而不是通用描述。
疑惑?下面代码为啥报错
#include<iostream>
template<typename T>
T add(T &a,T &b);
template double add<double>(double,double);//显示定义一个形参为double的函数定义
//,为什么报错
int main()
{
double a = 11.5;double b = 11;
//std::cout << add<double>(a,b);
std::cout << add(a,b);
}
template<typename T>
T add(T &a,T &b)
{
return a+b;
}
本文介绍了C++中的模板功能,包括如何定义和使用函数模板,以及模板的重载。通过示例展示了如何交换不同类型的数据,以及如何为特定类型如`job`结构体实现显式具体化。此外,还讨论了模板的隐式实例化、显示实例化和显示具体化之间的区别,并给出了相关代码示例。
440





