函数模板的多个定义

时间:2008-10-24 23:44:24

标签: c++ templates linker

假设头文件定义了一个函数模板。现在假设有两个实现文件#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()的多个定义?
  • 标准中是否指定链接器必须正常处理此情况?换句话说,我是否可以始终依靠与上述类似的程序进行编译和链接?
  • 如果链接器可以足够聪明地消除一组函数模板实例化的歧义,为什么不能对常规函数执行相同操作,因为它们与实例化函数模板的情况相同?

3 个答案:

答案 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 ++没有实现它),链接器只接受多个定义。