编译整体过程(针对C/C++),
1. 预处理:包含宏替换,条件编译,include导入文件
2. 编译: 包含词法分析,语法分析,语义分析,中间代码生成与优化,生成汇编文件
3. 汇编: 将汇编文件编译成2进制的机器码
4. 链接: 将目标文件与外部符号进行链接,得到一个二进制可执行文件
当编译器遇到一个模板定义时,它并不生成代码。只有我们实例化出模板的一个特定版本时,编译器才会生成代码。
一般情况,当我们调用一个函数时,编译器只需要掌握函数的声明。当我们使用一个类类型的对象时,类的定义必须可用,但是成员函数的定义不一定已经出现。因此,我们把类的定义和函数声明放在头文件,普通函数和类的成员函数放在源文件中。
但是对于模板来说,为了生成一个实例化版本,编译器需要掌握函数模板或类模板成员函数的定义,因此,我们也将模板的定义放在头文件里面。
模板编译错误时机
模板由于在实例化时才生成代码,所以这会影响其编译错误的时机。
一般来说分为三个阶段,
第一, 编译模板本身,这里主要是语法错误,就是词法分析么~
第二, 遇到模板使用,这个阶段主要检查实参数目,类型匹配等。
第三, 模板实例化,这个阶段最容易暴露问题,根据编译器管理实例化情况,可能在链接时才出现错误。
可能大家对第二个和第三个阶段区别上比较困难,我们这样理解:
第一阶段,词法分析就是关键字,标识符什么的有没有拼写错误。
第二阶段,遇到模板就类似于语法分析,语法分析就是检查你这个句子是否合理通顺被,参数数量不对很明显不符合语法对吧
第三阶段,实例化就依赖于编译器,不过我们这里可以理解为在语义分析之后,你的代码可能存在类型不匹配的情况,只有在真正执行代码的时候才会发现,所以可能出现在链接执行的时候。
比如一个返回最大值的函数max
Template
{
If(a
If(a>b) return a;
If(a==b) retun a;
}
我们定义一个类 DataNum da1,da2;
Max(da1,da2);
结果在实例化的时候就可能是错误的,因为我们没有给DataNum类定义重载运算符“<”
所以,由于模板被使用时才会实例化这一特性,相同的实例可以出现在多个文件对象中。这样对大文件系统可能会产生很大的开销,C++11新标准中可以通过显示实例化来避免。
Blob.cpp
extern template class Blob
extern template int compare(const &int,const&int);
Blob
Blob
Blob
Int I =compare(bs1[1],bs1[2]);
Template.cpp
template int compare(const &int,const &int);
template class Blob
由于在Blob.cpp文件中使用了extern,所以其实例化的位置在其他文件,也就是Template.cpp.
而bs1,bs2是int类型的,没有extern声明,所以都是在Blob里面就已经实例化了。