###震惊 **C++竟然不支持模板的分离编译**

本文详细介绍了C++中函数和模板的编译及连接过程。解释了编译器如何处理函数声明与定义分离的情况,并重点分析了模板类与模板函数在编译时的行为差异及其解决方案。

在一个编译单元里,我们假设有三个文件,test.h test.cpp main.cpp。test.h文件里装有各个函数的声明,而实现在test.cpp中,main.cpp中实例化函数。当编译器将一个工程里的所有.cpp文件以分离的方式编译完毕后,再由连接器(linker)进行连接成为一个.exe文件。

test.h
void FUNC();
===========================
test.cpp
#include"test.h"
void FUNC()
{
 dosomthing;
}
===========================
main.cpp
#include"test.h"
int main()
{
 FUNC();
 return 0;
}

其实在执行此程序时会发生一下过程。
test.cpp 与 main.cpp在编译的过程中,成为test.obj和 main.obj,在执行main.cpp中调用了FUNC()函数,此时编译器仅仅知道main.cpp中所包含的test.h文件中一个关于FUNC()函数的声明。编译器将这里FUNC()看做外部链接类型,就是认为他的函数实现在另一个.obj文件中,编译时main.cpp中对FUNC()的调用只会生成一句call指令,此时的call指令其实是有问题的,因为main.cpp中没有实现FUNC()的代码。
这是连接器在其他的.obj文件中寻找FUNC()的实现代码,找到以后将call FUNC()的指令调运的地址换成实际FUNC()的进入点地址,注意的是:连接器实际上是将工程中的.obj连接为.exe,关键是在寻找外部链接符合在另一个.obj中的地址,然后替换原来的地址。

编译main.cpp时,编译器不知道FUNC的实现,所以当碰到对它的调用时只是给出一个指示,指示连接器应该为它寻找f的实现体。这也就是说main.obj中没有关于f的任何一行二进制代码。

编译test.cpp时,编译器找到了FUNC的实现。于是乎FUNC的实现(二进制代码)出现在test.obj里。

连接时,连接器在test.obj中找到FUNC的实现代码(二进制)的地址(通过符号导出表)。然后将main.obj中悬而未决的call XXX地址改成f实际的地址。完成。

然而模板却又不同
模板函数的代码并不能直接编译成二进制代码,而要有一个实例化的过程。

test.h
    template<class T>
    class A
    {
    public:
    void f();//只声明
    }

    test.cpp
    #include"test.h"
    template<class T>
    void A<T> f()
    {
    dosomthing;
    }

    main.cpp
    #include"test.h"
    int main()
    {
     A<int> a;
     f();
    }

编译器在main.cpp中并不知道Aa的定义,因为它并不在test.h中,而当连接器在其他的.obj中寻找时,也并没有,因为模板只有用到的时候才会被实例化出来,显然test.cpp中并没有用,于是只能给出错误。

这里写图片描述

解决方法

在模板头文件 x x x . h 里面显示实例化-> 模板类的定义后面添加 template class AA; 一般不推荐这种方法,一方面老编
译器可能不支持,另一方面实例化依赖调用者。(不推荐)
2 . 将声明和定义放到一个文件 ” x x x . hpp” 里面,推荐使用这种方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值