C++ 函数模板与分离编译模式

代码编译运行环境:VS2017+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 "func.h"
#include <iostream>
using namespace std;

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);
}
/***end main.cpp***/

这是一个结构非常清晰的程序,但是它不能通过编译。在VS2017下的出错信息是:

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)不符合分离编译模式的规则,因为分离编译模式要求函数原型申明放在头文件,定义放在源文件。

注意: 这样做,如果在多个目标文件中存在相同的函数模板实例化后的模板函数实体,链接时并不会报函数重定义的错误,这与普通函数不同,因为编译器会对实例化后的重复的模板函数实体进行优化,只保留一份代码实体。如果不同的源文件中都保留一份函数模板实体,会造成代码冗余,实际上,这也是一种代码冗余的解决办法。

3.2 仍然采用分离编译模式

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

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

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

3.3 显示实例化

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

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

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

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


参考文献

[1] 陈刚.C++高级进阶教程[M].武汉:武汉大学出版社,2008.C6.5模板与分离编译模式.P223-P225
[2] 深入理解C++11[M].北京:机械工业出版社.C2.12外部模板.P50-P54

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值