何时发生隐式模板实例化?

时间:2016-11-15 06:14:42

标签: c++

我想知道在以下情况下何时/何处发生隐式模板实例化。

// temp.h
template <typename T>
struct A {
    T value;
}
// foo.h
#include "temp.h"
void foo();
// foo.cpp
#include "foo.h"
void foo() { A<int> _foo; }
// bar.h
#include "temp.h"
void bar();
// bar.cpp
#include "bar.h"
void bar() { A<int> _bar; }
// main.cpp
#include "foo.h"
#include "bar.h"
int main() { foo(); bar(); return 0; }

我认为在调用foo()时会发生这种情况,因为它是第一次使用A<int>,因此A<int>foo.o实现的。 并且,在调用bar()时,它会在A<int>foo.o相关联。

我是对的吗?或实例化发生两次?

2 个答案:

答案 0 :(得分:4)

标准没有说明编译器应如何隐式实例化模板。

我不确定其他编译器,这就是g ++处理它的方式,来自7.5 Where's the Template?

  

不知何故,编译器和链接器必须确保每个模板实例在可执行文件中只发生一次(如果需要),否则就完全没有。这个问题有两种基本方法,称为Borland模型和Cfront模型。

     
      
  • Borland模特:
  •   
     
    

Borland C ++通过向其链接器添加等效公共块的代码来解决模板实例化问题;编译器在使用它们的每个转换单元中发出模板实例,并且链接器将它们折叠在一起。这个模型的优点是链接器只需要考虑目标文件本身;无需担心外部复杂性。缺点是编译时间增加,因为模板代码是重复编译的。为此模型编写的代码往往包含头文件中所有模板的定义,因为必须看到它们被实例化。

  
     
      
  • Cfront模型:
  •   
     
    

AT&amp; T C ++翻译器Cfront通过创建模板存储库的概念解决了模板实例化问题,模板存储库是存储模板实例的自动维护位置。存储库的更现代版本的工作方式如下:在构建单个目标文件时,编译器会放置存储库中遇到的任何模板定义和实例化。在链接时,链接包装器会添加存储库中的对象,并编译以前未发出的任何所需实例。该模型的优点是更优化的编译速度和使用系统链接器的能力;要实现Borland模型,编译器供应商也需要替换链接器。缺点是复杂性大大增加,因此容易出错;对于某些代码,这可以是透明的,但实际上,在一个目录中构建多个程序并在多个目录中构建一个程序可能非常困难。为此模型编写的代码倾向于将非内联成员模板的定义分成单独的文件,该文件应单独编译。

  

这是g ++实现它的方式,重点是我的:

  

G ++在链接器支持的目标上实现Borland模型,包括ELF目标(如GNU / Linux),Mac OS X和Microsoft Windows。否则G ++不实现自动模型。

那就是说:用g ++,每个翻译单元都有自己的实例化。该页面再次提到:

  

...,但每个翻译单元都包含它使用的每个模板的实例。链接器将丢弃重复的实例,但在大型程序中,这可能导致目标文件或共享库中的代码重复数量无法接受。

你可以选择避免它(当然是第一个和最后一个选项是明确的):

  
      
  1. 通过在一个目标文件中定义显式实例化,并通过使用外部模板语法<使用显式实例化声明阻止编译器在任何其他目标文件中执行隐式实例化,可以避免模板的重复实例< / p>

  2.   
  3. 使用-frepo编译模板使用代码。编译器生成扩展名为.rpo的文件,其中列出了可在那里实例化的相应目标文件中使用的所有模板实例化;然后,链接包装器collect2更新.rpo文件,告诉编译器放置这些实例的位置并重建任何受影响的目标文件。第一次传递后,链接时间开销可以忽略不计,因为编译器会继续将实例化放在相同的文件中。

  4.   
  5. 使用-fno-implicit-templates编译代码以禁用隐式生成模板实例,并显式实例化您使用的所有模板实例。

  6.   

答案 1 :(得分:2)

根据GNU C ++编译器文档(https://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html)&#34;每个翻译单元包含它使用的每个模板的实例&#34;。所以除非你未使用的_foo和_bar对象 优化出来,两个目标文件都应该有重复的实例。然后,&#34;链接&#34;将丢弃重复的实例,这通常意味着链接的顺序决定使用哪个实例。但最终结果在两个订单中都不会有任何不同。