函数模板的多个定义
Multiple definitions of a function template
>假设一个头文件定义了一个函数模板。 现在假设有两个实现文件#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()
的多个定义? - 标准中是否指定链接器必须正常处理这种情况? 换句话说,我总是可以依靠类似于上述的程序来编译和链接吗?
- 如果链接器可以足够聪明地消除一组函数模板实例化的歧义,那么为什么它不能对常规函数执行相同的操作,因为它们与实例化函数模板的情况相同?
Gnu C++编译器手册对此进行了很好的讨论。 摘录:
C++模板是第一语言 需要更多智能的功能 从环境比一个通常 在 UNIX 系统上找到。不知何故, 编译器和链接器必须确保 每个模板实例都发生 在可执行文件中恰好一次,如果它 是需要的,而不是其他的。 对此有两种基本方法 问题,称为 博兰德模型和克弗兰德模型。
博兰德模型
博兰德C++解决了模板 通过添加实例化问题 代码等效于公共块 他们的接头;编译器发出 每个翻译中的模板实例 使用它们的单元和链接器 将它们折叠在一起。优势 这个模型是链接器只有 必须考虑目标文件 他们自己;没有外部 要担心的复杂性。这 缺点是编译时间 增加,因为模板代码 正在重复编译。法典 为此模型编写的倾向于 包括所有模板的定义 在头文件中,因为它们必须是 看到被实例化。
正面模型
AT&T C++翻译,Cfront, 解决了模板实例化问题 通过创建 模板存储库,自动 维护模板的地方 实例被存储。更现代 存储库的版本工作为 如下:作为单个对象文件 构建,编译器放置任何 模板定义和 在 存储 库。在链接时,链接 包装器将对象添加到 存储库并编译任何需要的内容 以前没有的实例 排放。该模型的优点 更优化的编译速度和 使用系统链接器的能力; 实施博兰德模型 a 编译器供应商还需要替换 链接器。缺点是 大大增加了复杂性,因此 潜在的错误;对于某些代码 这可以同样透明,但是 在实践中,这可能非常困难 将多个程序构建在一个程序中 目录和多个程序中的一个程序 目录。为此编写的代码 模型倾向于分离定义 将非内联成员模板放入 单独的文件,应该是 单独编译。
当与 GNU ld 版本 2.8 或 一起使用时 后来在 ELF 系统上,例如 GNU/Linux 或 Solaris 2,或 Microsoft Windows,G++支持: 博兰德模型。在其他系统上,G++ 不实现任何自动模型。
为了支持C++,链接器足够聪明,可以识别它们都是相同的函数,并抛弃除一个之外的所有函数。
编辑:澄清:链接器不会比较函数内容并确定它们是否相同。模板化函数被标记为这样,链接器识别出它们具有相同的签名。
这或多或少是模板的特殊情况。
编译器仅生成实际使用的模板实例化。由于它无法控制从其他源文件生成哪些代码,因此它必须为每个文件生成一次模板代码,以确保生成该方法。
由于很难解决这个问题(该标准有一个模板的extern
关键字,但 g++ 没有实现它),链接器只是接受多个定义。
- 仅在函数模板中为那些定义了函数的类型执行函数
- 在 C++20 中是否不再允许在 std 中对程序定义类型的函数模板进行专用化?
- 使用定义函数模板别名
- 何时需要实例化函数模板定义?
- 是否可以使用单个定义定义函数的常量和常规版本?(使用模板,自动,decltype等)
- 定义类模板构造函数的两种方法之间的区别
- 使用表达式 SFINAE 的函数模板的类外定义
- 如何为自定义模板对象创建专门的函数模板
- 我应该声明我的函数模板专业化还是定义它们就足够了
- 使用用户定义的转换运算符推导函数模板参数
- 如何定义函数模板中使用的函数?
- 为什么用户定义的转换函数模板不能有推导的返回类型?
- C++,当函子不是一个选项时,我如何编写带有自定义函数调用的模板化 RAII 包装器?
- 关于函数模板中定义的 lambda 闭包类型可以说些什么?
- 错误:重新定义函数模板(或 C2995)
- 调用标头中定义的模板函数时C++链接器错误
- 模板中的自定义函数C++
- 如何严格定义函数模板显式实例化规则
- 定义函数模板
- 错误C2995:已定义函数模板