首页 > 代码库 > 模板与分离编译模式

模板与分离编译模式

代码编译运行环境:VS2012+Debug+Win32


1.分离编译模式

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件连接起来形成单一的可执行文件的过程成为分离编译模式。


2.使用模板在连接时出错

在C++程序设计中,在一个源文件中定义某个函数,然后在另一个源文件中使用该函数,这是一种非常普遍的做法。但是,如果定义和调用一个函数模板时也采用这种方式,会发生编译错误。

下面的程序由三个文件组成:func.h用来对函数模板进行申明,func.cpp用来定义函数模板,main.cpp包含func.h头文件并调用相应的函数模板。

/***func.h***/
template<class T> void func(const T&);
/***end func.h***/

/***func.cpp***/
#include <iostream>
using namespace std;

#include "func.h"
template<class T> void func(const T& t){
    cout<<t<<endl;
}
/***end func.cpp***/

/***main.cpp***/
#include <stdio.h>
#include "func.h"

int main(){
    func(3);
    getchar();
}
/***end main.cpp***/

这是一个结构非常清晰的程序,但是它不能通过编译。在VS2012下的出错信息是:
error LNK2019: 无法解析的外部符号 “void __cdecl func< int>(int const &)” (??$func@H@@YAXABH@Z)。这是怎么回事呢?

原因出现在分离编译模式上。在分离编译模式下,func.cpp会生成一个目标文件为func.obj,由于在func.cpp文件中,并没有发生函数模板调用,所以不会将函数模板func< T>实例化为模板函数func< int>,也就是说,在func.obj中无法找到关于模板函数func< int>的实现代码。在源文件main.cpp中,虽然函数模板被调用,但由于没有模板代码,也不能将其实例化。也就是说,在main.obj中也找不到模板函数func< int>的实现代码。这样,在连接的时候就会出现func< int>没有定义的错误。


3.解决办法

3.1将函数模板的定义放到头文件中

一个简单的解决办法就是将函数模板func< T>的定义写到头文件func.h中。这样的话,只要包含了这个头文件,就会把函数模板的代码包含进来,一旦发生函数调用,就可以依据函数模板代码将其实例化。
这个办法虽然简单可行,但是有如下不足。
(1)函数模板的定义写进了头文件,暴露了函数模板的实现细节。

(2)不符合分离编译模式的规则,因为分离编译模式要求函数原型申明放在头文件,定义放在源文件。

注意:这样做,如果在多个目标文件中存在相同的函数模板实例化后的模板函数实体,连接时并不会报函数重定义的错误,这与普通函数不同,是C++对模板函数的特殊规定。

3.2仍然采用分离编译模式

有什么办法可以让函数模板实例化时能够找到相应的模板函数的代码呢?一个可能的解决办法就是使用关键字export。也就是说,在func.cpp里定义函数模板的时候,将函数模板头写成:

export template<class T> void func(const T& t);

这样做的目的是告诉编译器,这个函数模板可能再其他源文件中被实例化。这是一个对程序员来说负担最轻的解决办法,但是,目前几乎所有的编译器都不支持关键字export,包括VC++和g++。

3.3显示实例化

显示实例化也称为外部实例化。在不发生函数调用的时候将函数模板实例化,或者在不使用类模板的时候将类模板实例化称之为模板显示实例化。

上面遇到的问题是main.obj和func.obj中找不到模板函数func< int>的实现代码,那么就在func.cpp中将函数模板func< T>显示实例化为模板函数func< int>。

template void func<int>(cons tint&);//函数模板显示实例化

这样,就可以在func.cpp产生模板函数func< int>的实例化代码,编译之后就会产生函数的二进制代码,供其它源文件连接,程序就可以正常运行。当类模板的成员函数的实现定义在源文件中,通过模板类的对象调用成员函数时也会出现找不到函数定义的错误,可以使用同样的方法解决,不再赘述。


参考文献

[1]陈刚.C++高级进阶教程[M].武汉:武汉大学出版社,2008[6.5(P223-P225)]

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    模板与分离编译模式