简体   繁体   English

函数模板的多个定义

[英]Multiple definitions of a function template

Suppose a header file defines a function template. 假设头文件定义了一个函数模板。 Now suppose two implementation files #include this header, and each of them has a call to the function template. 现在假设有两个实现文件#include这个头文件,每个文件都调用了函数模板。 In both implementation files the function template is instantiated with the same type. 在两个实现文件中,函数模板都使用相同的类型进行实例化。

// 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);
}

One may expect the linker would complain about multiple definitions of f() . 人们可能会期望链接器会抱怨f()多个定义。 Specifically, if f() wouldn't be a template then that would indeed be the case. 具体来说,如果f()不是模板,那确实就是这种情况。

  • How come the linker doesn't complain about multiple definitions of f() ? 为什么链接器不会抱怨f()多个定义?
  • Is it specified in the standard that the linker must handle this situation gracefully? 是否在标准中指定链接器必须优雅地处理这种情况? In other words, can I always count on programs similar to the above to compile and link? 换句话说,我是否可以始终依靠与上述类似的程序进行编译和链接?
  • If the linker can be clever enough to disambiguate a set of function template instantiations, why can't it do the same for regular functions, given they are identical as is the case for instantiated function templates? 如果链接器可以足够聪明地消除一组函数模板实例化的歧义,为什么它不能对常规函数执行相同的操作,因为它们与实例化的函数模板的情况相同?

In order to support C++, the linker is smart enough to recognize that they are all the same function and throws out all but one. 为了支持C ++,链接器足够聪明,可以识别出它们都是相同的功能,并抛弃除了一个以外的所有功能。

EDIT: clarification: The linker doesn't compare function contents and determine that they are the same. 编辑:澄清:链接器不比较函数内容并确定它们是相同的。 Templated functions are marked as such and the linker recognizes that they have the same signatures. 模板化函数标记为这样,链接器识别它们具有相同的签名。

The Gnu C++ compiler's manual has a good discussion of this . Gnu C ++编译器手册对此进行了很好的讨论 An excerpt: 摘录:

C++ templates are the first language feature to require more intelligence from the environment than one usually finds on a UNIX system. 与通常在UNIX系统上找到的相比,C ++模板是第一个需要从环境中获取更多智能的语言功能。 Somehow the compiler and linker have to make sure that each template instance occurs exactly once in the executable if it is needed, and not at all otherwise. 不知何故,如果需要,编译器和链接器必须确保每个模板实例在可执行文件中只发生一次,否则就完全没有。 There are two basic approaches to this problem, which are referred to as the Borland model and the Cfront model. 这个问题有两种基本方法,称为Borland模型和Cfront模型。

Borland model Borland模型

Borland C++ solved the template instantiation problem by adding the code equivalent of common blocks to their linker; Borland C ++通过向其链接器添加等效公共块的代码来解决模板实例化问题; the compiler emits template instances in each translation unit that uses them, and the linker collapses them together. 编译器在使用它们的每个转换单元中发出模板实例,并且链接器将它们折叠在一起。 The advantage of this model is that the linker only has to consider the object files themselves; 这个模型的优点是链接器只需要考虑目标文件本身; there is no external complexity to worry about. 无需担心外部复杂性。 This disadvantage is that compilation time is increased because the template code is being compiled repeatedly. 这个缺点是编译时间增加,因为重复编译模板代码。 Code written for this model tends to include definitions of all templates in the header file, since they must be seen to be instantiated. 为此模型编写的代码往往包含头文件中所有模板的定义,因为必须看到它们被实例化。

Cfront model Cfront模型

The AT&T C++ translator, Cfront, solved the template instantiation problem by creating the notion of a template repository, an automatically maintained place where template instances are stored. AT&T C ++翻译器Cfront通过创建模板存储库的概念解决了模板实例化问题,模板存储库是存储模板实例的自动维护位置。 A more modern version of the repository works as follows: As individual object files are built, the compiler places any template definitions and instantiations encountered in the repository. 存储库的更现代版本的工作方式如下:在构建单个目标文件时,编译器会放置存储库中遇到的任何模板定义和实例化。 At link time, the link wrapper adds in the objects in the repository and compiles any needed instances that were not previously emitted. 在链接时,链接包装器会添加存储库中的对象,并编译以前未发出的任何所需实例。 The advantages of this model are more optimal compilation speed and the ability to use the system linker; 该模型的优点是更优化的编译速度和使用系统链接器的能力; to implement the Borland model a compiler vendor also needs to replace the linker. 要实现Borland模型,编译器供应商也需要替换链接器。 The disadvantages are vastly increased complexity, and thus potential for error; 缺点是复杂性大大增加,因此容易出错; for some code this can be just as transparent, but in practice it can be very difficult to build multiple programs in one directory and one program in multiple directories. 对于某些代码,这可以是透明的,但实际上,在一个目录中构建多个程序并在多个目录中构建一个程序可能非常困难。 Code written for this model tends to separate definitions of non-inline member templates into a separate file, which should be compiled separately. 为此模型编写的代码倾向于将非内联成员模板的定义分成单独的文件,该文件应单独编译。

When used with GNU ld version 2.8 or later on an ELF system such as GNU/Linux or Solaris 2, or on Microsoft Windows, G++ supports the Borland model. 当在GNU / Linux或Solaris 2等ELF系统上使用GNU ld 2.8或更高版本时,或者在Microsoft Windows上使用时,G ++支持Borland模型。 On other systems, G++ implements neither automatic model. 在其他系统上,G ++既不实现自动模型。

This is more or less a special case just for templates. 这或多或少只是模板的特例。

The compiler only generates the template instantiations that are actually used. 编译器仅生成实际使用的模板实例化。 Since it has no control over what code will be generated from other source files, it has to generate the template code once for each file, to make sure that the method gets generated at all. 由于无法控制从其他源文件生成的代码,因此必须为每个文件生成一次模板代码,以确保生成该方法。

Since it's difficult to solve this (the standard has an extern keyword for templates, but g++ doesn't implement it) the linker simply accepts the multiple definitions. 由于很难解决这个问题(标准有一个模板的extern关键字,但是g ++没有实现它),链接器只接受多个定义。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM