为什么模板不支持分离编译?

一.什么是分离编译模式?
分离编译模式是指一个程序由若干个源文件共同实现,每个源文件单独编译生成目标文件,然后再将所有的目标文件链接起来生成单一的可执行程序的这么一个过程。
*在实际开发大型项目的时候,不可能把所有的源程序都放在一个头文件中,而是分别由不同的程序员开发不同的模块,再将这些模块汇总成为最终的可执行程序。
二.程序编译过程。
为什么模板不支持分离编译?
解说:
简单的说就是生成源文件即由#include中的.h文件和test.cpp文件组成编译单元(其中.h文件的代码被扩展到包含.h的.cpp文件里),然后编译器把.cpp文件编译为目标文件.obj文件(文件中有可执行文件的格式且已是二进制码),当编译器把所有的.cpp文件分离编译完成.obj文件,最后再由连接器(linker)把这些.obj文件连接起来形成可执行文件.exe文件。

例:
为什么模板不支持分离编译?
在main.cpp中,调用了func()函数,然而当编译器编译main.cpp时,它仅知道的只是main.cpp中所包含的test.h文件中的一个关于void func();的声明,main.obj中没有关于func()的任何一行二进制代码(main.obj中对func()的调用只会生成一行call指令)。所以,编译器将这里的func()看作外部连接类型,即认为它的函数实现代码在另一个.obj文件中,也就是test.obj(此文件中存在二进制代码)。连接时,连接器在test.obj中找到func()的实现代码(二进制)的地址(通过符号导出表)。然后将main.obj中call XXX地址改成func()实际的地址。

三:为什么模板不支持分离编译呢?
——编译时没有实例化,会导致链接错误,即模板函数的代码并不能直接编译成二进制代码,必须有一个实例化的过程。
例:
为什么模板不支持分离编译?
编译器在main.cpp处并不知道A::f的定义,因为它不在test.h里面,于是编译器只好寄希望于连接器,希望它能够在其他.obj里面找到A::f的实例,即test.obj,然而,后者中没有A::f的二进制代码,因为C++标准明确表示,当一个模板不被用到的时侯它就不该被实例化出来,test.cpp中没有用到A::f,所以实际上test.cpp编译出来的test.obj文件中关于A::f一行二进制代码也没有,于是连接器只好给出一个连接错误。
*解决办法:将声明和定义放在一个文件“xxx.hpp”(test.h&&test.cpp).
即在test.cpp中写一个函数,其中调用A::f,则编译器会将其实例化出来,因为在这个点上(test.cpp中),编译器知道模板的定义,所以能够实例化,test.obj的符号导出表中就有了A::f这个符号的地址,于是连接器就能够完成任务。
**在分离式编译的环境下,编译器编译某一个.cpp文件时并不知道另一个.cpp文件的存在,也不会去查找(当遇到未决符号时它会寄希望于连接器)。这种模式在没有模板的情况下运行良好,但遇到模板时就傻眼了,因为模板仅在需要的时候才会实例化出来,所以,当编译器只看到模板的声明时,它不能实例化该模板,只能创建一个具有外部连接的符号并期待连接器能够将符号的地址决议出来。然而当实现该模板的.cpp文件中没有用到模板的实例时,编译器懒得去实例化,所以,整个工程的.obj中就找不到一行模板实例的二进制代码了。

参考文献:http://blog.csdn.net/pongba/article/details/19130
http://blog.csdn.net/K346K346/article/details/48879021