在C++中,模板(template)和外部链接(extern)关键字通常用于解决在多个文件中使用模板时可能出现的链接错误。
当我们定义一个模板时,编译器不会为其生成实际的代码,而是在需要使用该模板的地方根据实际类型生成代码。这意味着,如果我们在多个源文件中使用同一个模板,每个文件都会生成自己的版本,这可能会导致链接错误。
为了避免这种情况,我们可以使用extern关键字来告诉编译器,该模板的定义位于其他文件中,我们只需要在当前文件中声明该模板即可。这样编译器就不会在当前文件中生成模板的实现,而是在链接时从其他文件中获取。
下面是一个例子:
假设我们有一个头文件template.h,其中定义了一个模板类template_class:
// template.h
#ifndef TEMPLATE_H
#define TEMPLATE_H
template <typename T>
class template_class {
public:
void do_something(T value);
};
#endif
我们在两个源文件中都使用了该模板:
// main1.cpp
#include "template.h"
int main() {
template_class<int> obj;
obj.do_something(42);
return 0;
}
// main2.cpp
#include "template.h"
template_class<double> obj;
int main() {
obj.do_something(3.14);
return 0;
}
如果我们尝试编译这两个源文件,会得到链接错误:
Undefined symbols for architecture x86_64:
"void template_class<int>::do_something(int)", referenced from:
_main in main1.o
"void template_class<double>::do_something(double)", referenced from:
_main in main2.o
ld: symbol(s) not found for architecture x86_64
为了解决这个问题,我们可以将模板定义移到一个单独的源文件template.cpp中,并使用extern关键字来声明它:
// template.cpp
#include "template.h"
template <typename T>
void template_class<T>::do_something(T value) {
// implementation goes here
}
template class template_class<int>;
template class template_class<double>; // 模板显式实例化
在这个文件中,我们定义了模板的实现,并显式实例化了模板类template_class和template_class,以便编译器知道在链接时需要从哪里获取它们的实现。
然后,我们在每个源文件中使用extern关键字来声明模板:
// main1.cpp
#include "template.h"
extern template class template_class<int>; // 使用外部模板
int main() {
template_class<int> obj;
obj.do_something(42);
return 0;
}
// main2.cpp
#include "template.h"
extern template class template_class<double>;
template_class<double> obj;
int main() {
obj.do_something(3.14);
return 0;
}
现在,如果我们编译这两个源文件,就不会得到链接错误了。