首先明确什么是“外部”(extern)。比如a.c文件中有个int i,而另一个文件b.c文件中想使用i这个变量,则需要在b.c文件中做一个声明:
extern int i;
这样的好处是,在分别编译了a.c和b.c之后,其生成的目标文件a.o和b.o中只有i这个符号的一份定义。a.o中的i是实在存在于a.o目标文件的数据区中的数据,而在b.o中,只是记录了i符号会引用其他目标文件中数据区中的名为i的数据。这样一来,在链接器将a.o和b.o链接成单个可执行文件(或者库文件)c的时候,c文件的数据区也只会有一个i的数据。
而如果b.c中声明int i的时候不加上extern,那么i就会实在存在于a.o和b.o的数据区中,链接器在链接a.o和b.o的时候,就会报错,因为无法决定相同的符号是否需要合并。
而对于函数模板,比如在test.h的文件中声明如下:
template<typename T> void fun(T){}
在test1.cpp中,定义如下
#include "test.h"
void test1(){
fun(3);
}
在test2.cpp中,定义如下
#include "test.h"
void test2(){
fun(4);
}
由于两个源代码模板函数参数类型一致,所以实例化出了两个一样的函数fun<int>(int),代码重复,链接器会只保留一份。如果源代码中有过多的模板,编译器会做很多实例化工作,链接器又会做很多去重工作,会导致编译时间和链接时间过长。为了解决这个问题,引入了“外部”模板。
在C++11中,我们可以使用如下声明,针对上面的代码,我们可以,在test1.cpp中做显示的实例化:
#include "test.h"
template void fun<int>(int);//显示的实例化
void test1(){
fun(3);
}
在test2.cpp中,做外部模板声明:
#include "test.h"
extern template void fun<int>(int);//外部模板的声明
void test2(){
fun(4);
}
需要注意,外部模板声明不能用于一个静态函数,但可以用于类静态成员函数。