假设头文件定义了一个函数模板。现在假设有两个实现文件#include
这个头,并且每个文件都调用了函数模板。在两个实现文件中,函数模板都使用相同的类型进行实例化。
// header.hh
template <typename T>
void f(const T& o)
{
// ...
}
// impl1.cc
#include "header.hh"
void fimpl1()
{
f(42);
}
// impl2.cc
#include "header.hh"
void fimpl2()
{
f(24);
}
有人可能会认为链接器会抱怨f()
的多个定义。具体来说,如果f()
不是模板,那确实就是这种情况。
f()
的多个定义?答案 0 :(得分:5)
为了支持C ++,链接器足够智能,可以识别出它们都是相同的功能,并抛出除了一个以外的所有功能。
编辑:澄清: 链接器不比较函数内容并确定它们是相同的。 模板化函数标记为这样,链接器识别它们具有相同的签名。
答案 1 :(得分:4)
Gnu C ++编译器手册有a good discussion of this。摘录:
C ++模板是第一种语言 需要更多智能的功能 从环境比通常 在UNIX系统上找到。不知何故 编译器和链接器必须确保 每个模板实例都会发生 在可执行文件中只有一次 需要,而不是其他。 有两种基本方法 问题,这被称为 Borland模型和Cfront模型。
Borland模型
Borland C ++解决了模板 通过添加实例化问题 相当于公共块的代码 他们的接头;编译器发出 每个翻译中的模板实例 使用它们的单元和链接器 将它们折叠在一起。优势 这个模型只是链接器 必须考虑目标文件 他们自己;没有外在的 担心的复杂性。这个 缺点是编译时间 由于模板代码增加了 正在反复编译。码 为这个模型写的倾向于 包括所有模板的定义 在头文件中,因为它们必须是 看来是被实例化了。
Cfront模型
AT&amp; T C ++翻译,Cfront, 解决了模板实例化问题 通过创建一个概念的问题 模板库,自动生成 保持模板的地方 实例存储。更现代 存储库的版本作为 如下:作为单个对象文件 是构建的,编译器放置任何 模板定义和 在实例中遇到的实例化 库。在链接时,链接 包装器添加了对象 存储库并编译任何所需的 之前没有的实例 发射。这种模式的优点 更优化的编译速度和 能够使用系统链接器; 实施Borland模型a 编译器供应商也需要更换 链接器。缺点是 大大增加了复杂性,因此 可能出错;对于一些代码 这可以同样透明,但是 在实践中,这可能非常困难 在一个中构建多个程序 目录和一个多个程序 目录。为此编写的代码 模型倾向于分开定义 非内联成员模板到 单独的文件,应该是 单独编译。
与GNU ld 2.8或更高版本一起使用时 后来在ELF系统上如 GNU / Linux或Solaris 2,或者 Microsoft Windows,G ++支持 Borland模型。在其他系统上,G ++ 既不实现自动模型。
答案 2 :(得分:1)
这或多或少只是模板的特殊情况。
编译器仅生成实际使用的模板实例化。由于它无法控制从其他源文件生成的代码,因此必须为每个文件生成一次模板代码,以确保生成该方法。
由于很难解决这个问题(标准对模板有extern
关键字,但g ++没有实现它),链接器只接受多个定义。