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

Hyu*_*won 7 c++

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

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

我认为它是在foo()被调用时发生的,因为它是第一次使用A<int>,因此A<int>实现于foo.o.
并且,当bar()被调用时,它与A<int>at 相关联foo.o.

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

Dan*_*anh 6

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

我不确定其他编译器,这是 g++ 处理它的方式,从7.5 模板在哪里?

如果需要,编译器和链接器必须以某种方式确保每个模板实例在可执行文件中只出现一次,否则根本不会出现。这个问题有两种基本方法,分别称为 Borland 模型和 Cfront 模型。

  • 宝兰型号:

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

  • Cfront型号:

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

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

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

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

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

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

  1. 可以通过在一个目标文件中定义显式实例化来避免模板的重复实例,并使用 extern 模板语法通过使用显式实例化声明来防止编译器在任何其他目标文件中进行隐式实例化

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

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