首先我要介绍分离编译的概念?
一个程序由若干个原文件共同实现,每个源文件单独编译生成目标文件,然后再将所有的目标文件链接起来生成单一的可执行程序的过程.ps.我所理解的分离编译就是不在同一个.h或.cpp文件中定义和实现.
程序编程过程如图所示
生成的源文件即在对应的.h和.cpp文件组成编译单元(其中.h的代码被扩展到包含.h的.cpp文件里),然后编译器把.cpp问津编译为目标文件.obj文件(文件中有可执行文件的格式且已经是二进制码的形式),当编译器吧所有的.cpp文件分离编译完成.obj文件,周再由链接器把这些.obj文件链接起来形成可执行文件.exe文件.
为什么模板不支持分离编译
编译没有实例化,会导致链接错误,即模板函数的代码并不能直接变异成二进制代码,必须有一个实例化的过程.
例
------text.h----
template<class T>
class A
{
pubclic:
void fun();
}
------text.cpp----
#include"text.h"
tempalte<class T>
void A<T>::fun()
{
...
}
------main.cpp----
#include"text.h"
int main()
{
A<int>a;
fun();
}
编译器在main.cpp处并不知道A::fun的定义,因为它不在text.h里面,于是俺一起只好寄希望于连接器,希望它能狗仔其他的.obj里面找到A::fun的实例,即test.obj,然而,后者中没有A::fun的二进制代码,因为C++标准明确指出,当一个模板不被用到的时候它就不应该被实例化出来,text.cpp中没有用到A::fu,所以实际上text.cpp编译出来的text.obj文件中关于A::fun 一行二进制代码也没有于是连接器只好给出一个链接错误.
解决办法:将声明和定义放在一个文件”xxx.hpp(.h&.cpp)内”
即在test.cpp中写一个函数,其中调用A::f,则编译器会将其实例化出来,因为在这个点上(test.cpp中),编译器知道模板的定义,所以能够实例化,test.obj的符号导出表中就有了A::f这个符号的地址,于是连接器就能够完成任务。
**在分离式编译的环境下,编译器编译某一个.cpp文件时并不知道另一个.cpp文件的存在,也不会去查找(当遇到未决符号时它会寄希望于连接器)。这种模式在没有模板的情况下运行良好,但遇到模板时就傻眼了,因为模板仅在需要的时候才会实例化出来,所以,当编译器只看到模板的声明时,它不能实例化该模板,只能创建一个具有外部连接的符号并期待连接器能够将符号的地址决议出来。然而当实现该模板的.cpp文件中没有用到模板的实例时,编译器懒得去实例化,所以,整个工程的.obj中就找不到一行模板实例的二进制代码了。