“模板”学习笔记(5)-----编译器在处理函数模板的时候都干了啥
=================编译器处理函数模板的方法:实例化模板函数====================
C++的编译器在遇到一个函数模板的时候,它首先要做的是确定该函数的参数,这就叫做把一个函数模板修改成一个具有具体参数的函数。这种方法一般而言是隐式转换的,我们一般是看不见的,但是C++也提供了一种显示转换的方法,如下:
template void Swap<int>(int &a,int &b);
这就是实例化模板函数的声明,上面这个式子把Swap()函数实例化为一个接受整型变量的函数。它与具体化函数模板不同之处在于:
- 具体化函数模板告诉了编译器利用此模板生成一个具体化了参数类型的重载函数,这样我们就可以通过该函数的定义来修改这个这个重载的函数,从而达到功能升级的目的。比如说在《“模板”学习笔记(4)-----如何解决函数模板的重载问题》中,我们就在程序中重载了这个Swap()函数,使它能够在面对结构体people类型的变量时,只交换他们的年龄(age),而不交换他们的名字。
- 实例化模板函数就不是这样。编译器根据其他已有模板的定义部分创建参数类型为一个准确类型的参数,这里实例化模板函数的作用只在于根据传递进来的具体参数创建一个具体的重载函数,而不会改变其定义的功能。比如说,Swap()模板函数接受的参数为ElementType &a和ElementType &b,而我们要传递两个整型变量x和y的时候,编译器就会自动将Swap()函数声明成这样:
void Swap(int &x,int &y);
这就表示编译器根据最初的函数模板Swap()生成了一个关于该Swap()函数的实例,这个过程就叫实例化模板函数。
通过上面的比较我们就可以清楚的看出来,具体化函数模板其实是一个有针对性的构思,而实例化模板函数却是一个行为,一个真真切切的动作。编译器会根据我们定义的模板在最终生成一个实例化之后的模板函数,我们就把这个由函数模板生成的函数叫做模板实例。而模板的定义在编译前是不会生成这样实例化的函数的,它只用于生成该函数的一个解决方案,^_^,也就是说,它为编译器生成一个认为可行的思路。所以说:
- 显式实例化模板函数是在编译时根据某个非具体参数类型的函数模板创建了一个具体参数类型的完整函数,由于他只是具体化了函数的参数类型,并没有修改函数的功能,因此没有必要,也不能够重写函数的定义部分;
- 具体化函数模板则即具体化了函数的参数类型,又要修改函数的功能。
p.s. 如果我们实例化模板函数之后还修改了其定义,那么编译器就会报错,程序如下:
#include <iostream> using namespace std; struct people { char name[10]; int age; }; template<class ElementType> void Swap(ElementType &a,ElementType &b) { ElementType temp; temp=b; b=a; a=temp; } //template<>void Swap<people>(people &a,people &b) void Swap<people>(people &a,people &b) { int temp; temp=b.age; b.age=a.age; a.age=temp; } int main() { int x=1,y=2; cout<<"交换之前,x="<<x<<"\ty="<<y<<endl; Swap(x,y); cout<<"交换之后,x="<<x<<"\ty="<<y<<endl; people person_1={"unqiue",22}; people person_2={"jack",30}; cout<<"交换之前,第一个人的名字叫:"<<person_1.name<<"\t年龄为:"<<person_1.age<<endl; cout<<"交换之前,第二个人的名字叫:"<<person_2.name<<"\t年龄为:"<<person_2.age<<endl; Swap(person_1,person_2); cout<<"调用具体化为people的Swap()函数的结果:"<<endl; cout<<"交换之后,第一个人的名字叫:"<<person_1.name<<"\t年龄为:"<<person_1.age<<endl; cout<<"交换之后,第二个人的名字叫:"<<person_2.name<<"\t年龄为:"<<person_2.age<<endl; return 0; }
生成的错误指向19行,错误内容就是““Swap”: 非法使用显式模板参数”。