c++不接受模板分离式编译的原因及其解决方法

首先呢,先了解一下什么是c++分离式编译。就比如说,我们要写一个非模板类A,会把类的声明放在test.h文件里面,再把该类的定义(即对类的实现)放在test.cpp中,并再test.cpp开头增加语句#include"test.h"这种方式。最后,再main.cpp中再对类对象实例化。这是c++程序员比较乐意接受的一种类编写方式,这也符合一般头文件规则:不要在分配存储空间前放置任何东西这条规则是为了防止在连接时的多重定义错误。

为什么C++编译器不能支持对模板的分离式编译 ???

首先,C++标准中提到,一个编译单元[translation unit]是指一个.cpp文件以及它所include的所有.h文件,.h文件里的代码将会被扩展到包含它的.cpp文件里,然后编译器编译该.cpp文件为一个.obj文件,后者拥有PE[PortableExecutable,windows可执行文件]文件格式,并且本身包含的就已经是二进制码,但是,不一定能够执行,因为并不保证其中一定有main函数。当编译器将一个工程里的所有.cpp文件以分离的方式编译完毕后,再由连接器(linker)进行连接成为一个.exe文件。对于非模板类,这种分离式编译,编译器和连接器,完全可以胜任。

然而,对于模板,你知道,模板函数的代码其实并不能直接编译成二进制代码,其中要有一个具现化的过程。举个例子。

//----------main.cpp------// 
template<class T>
 
void f(T t)
 
{}
 
int main()
 
{
 
…//do something
 
f(10); //call f<int>
编译器在这里决定给f一个f<int>的具现体 
…//do other thing
 
}
 
也就是说,如果你在main.cpp文件中没有调用过ff也就得不到具现,从而main.obj中也就没有关于f的任意一行二进制代码!!如果你这样调用了: 
f(10); //f<int>
得以具现化出来 
f(10.0); //f<double>
得以具现化出来 
这样main.obj中也就有了f<int>,f<double>两个函数的二进制代码段。以此类推。 

但是呢,要创建特例,即模板“具现化”,编译器不但要看到模板的声明,还要看到模板的定义。模板实例化过程是迟钝的,即只能用函数的定义来实现实例化。

看下面的例子:[将模板和它的实现分离] 
//-------------test.h----------------//
 
template<class T>
 
class A
 
{
 
public:
 
void f(); //
这里只是个声明 
};
 
//---------------test.cpp-------------//
 
#include”test.h”
 
template<class T>
 
void A<T>::f() //
模板的实现,但注意:不是具现 
{
 
…//do something
 
}
 
//---------------main.cpp---------------//
 
#include”test.h”
 
int main()
 
{
 
A<int> a;
 
a. f(); 

//编译器在这里并不知道A<int>::f的定义,因为它不在test.h里面 
//于是编译器只好寄希望于连接器,希望它能够在其他.obj里面找到 
//A<int>::f的实现体,在本例中就是test.obj,然而,后者中真有A<int>::f的 
//二进制代码吗?NO!!!因为C++标准明确表示,当一个模板不被用到的时 
//
侯它就不该被具现出来test.cpp中用到了A<int>::f了吗?没有!!所以实 
//际上test.cpp编译出来的test.obj文件中关于A::f的一行二进制代码也没有 
//于是连接器就傻眼了,只好给出一个连接错误 
//但是,如果在test.cpp中写一个函数,其中调用A<int>::f,则编译器会将其//具现出来,因为在这个点上[test.cpp中],编译器知道模板的定义,所以能//够具现化,于是,test.obj的符号导出表中就有了A<int>::f这个符号的地 
//址,于是连接器就能够完成任务。 

}


关键是:在分离式编译的环境下,编译器编译某一个.cpp文件时并不知道另一个.cpp文件的存在,也不会去查找[当遇到未决符号时它会寄希望于连接器]。这种模式在没有模板的情况下运行良好,但遇到模板时就傻眼了,因为模板仅在需要的时候才会具现化出来,所以,当编译器只看到模板的声明时,它不能具现化该模板,只能创建一个具有外部连接的符号并期待连接器能够将符号的地址决议出来。然而当实现该模板的.cpp文件中没有用到模板的具现体时,编译器懒得去具现,所以,整个工程的.obj中就找不到一行模板具现体的二进制代码,于是连接器也无语了!!!


解决上面这个问题的通用解法是,我们将模板的定义包含进声明模板的头文件中。

1.对于我们的例子,我们可以通过将#include"test.cpp"添加到test.h文件尾部.

2.在每一个使用我们的模板的点cpp文件中包含test.cpp文件,来达到目的。

3.还有第三种方法,就是删掉test.cpp文件,并重写test.h文件,使它包含所有的模板声明与定义:


本博客参考链接:

http://wenku.baidu.com/link?url=fljFjmFyTPEdHMCH6QHPmAw3pK6i9gbcfwSIlBYcxDEOzo04emjDMQQpBvIGtALjFfDTNCVzU7MuNybR0_0pgjEapG2NospNmtEOLEVu8Dy




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值