C++模板理解问题

5

模板代码在使用模板函数时才会被编译。但是编译后的代码保存在哪里呢?它保存在调用该模板函数的对象文件中吗?

例如,main.cpp从文件test.h调用一个模板函数,编译器生成一个对象文件main.o, 模板函数是否在main.o文件中?因为模板代码没有被内联,对吗?


你为什么认为模板代码没有被内联?尝试启用优化编译一个简单的示例,并反汇编结果。 - Steve Jessop
5个回答

4

这完全取决于编译器的实现。大多数编译器将在类似cpp文件中生成代码以进行内联编译。有时,通过优化设置,一些编译器甚至会重用相同的代码而不是为每个cpp重新创建它。

因此,您需要查看编译器的文档以了解更多详细信息。


3
这是实际正确的答案,而不是被提问者所接受的答案。 - Edward Strange

3
是的,模板函数的代码在main.o文件中被输出。 它的一些部分可能会被内联,就像其他任何代码一样可能被内联,但通常情况下,模板代码会被输出到任何实例化该模板的文件中。可以将其视为首先实例化模板代码以生成普通的非模板函数,然后使用编译器应用于任何其他函数的内联和优化来编译这些函数。
当相同的模板实例化(例如std ::vector<int>)出现在多个编译单元(.cpp文件) 中时,会存在一些困难,因为代码在每个编译单元中都会实例化。 解决这种情况的方法有很多种,有时涉及到链接阶段的清理步骤,其中重复的模板实例化被解析为单个实例化;您的编译器手册可以提供更多关于它如何处理这种情况的信息。

因此,创建一个带有未使用的虚拟变量的模板类并不等同于创建一个将所有内容放在头文件中的类。后者将是内联的,而模板类取决于编译器对其进行的操作。 - hidayat
@hidayat:不,普通类和类模板适用相同的规则。 - Konrad Rudolph
关于重复项:我认为在目标文件格式中,有一些可以在链接时合并的部分是标准特性。我认为至少有一个系统将它们称为“COMDAT”部分:http://docsun.cites.uiuc.edu/sun_docs/C/solaris_9/SUNWdev/LLM/p44.html(如果我没记错,一些链接器甚至会检查这些部分是否相同以捕获一些错误。) - BCS

1

即使未实例化,模板代码也会被编译。否则,编译器不需要对此发出诊断:

template< typename T >
void f()
{
  blah
}

模板编译分为两个阶段。除了基本检查外,所有依赖于模板参数的内容只能在实例化模板并使用实际类型填充形式参数时进行检查。例如,在这里。

template< typename T >
void f()
{
  typename T::nested_type x;
}

T::nested_type 只能在模板实例化并为 T 给定实际类型后进行检查。然而,当编译器遇到模板定义时,会执行基本检查(“给定 T::nested_type 是一种类型,这是一个有效的变量定义吗?”)。这就是为什么需要 typename 的原因。根据 TT::nested_type 可能是 T 的成员名称或静态数据成员的名称,这将使 T::nested_type x; 成为语法错误。因此,我们必须告诉编译器将 T::nested_type 视为类型名称。


0

它总是内联的(意思是,它总是具有内联语义的内部链接)。实际上,就像内联函数一样,它可能根本没有被内联,但模板不是代码。它是“生成代码的模板”。因此,除了特殊情况,它通常会驻留在头文件中,详见下文。

曾经有一个想法,叫做“导出关键字”,用于制作其他东西。但它已从标准中删除。

特殊情况:您可以将模板实例化编译成对象文件,而无需使用它们。这是避免所有模板代码内联的唯一方法。操作方式如下:

template class std::vector<MyClass>;

这将强制编译器在当前位置实例化模板。C++0x将有一种语法来强制编译器这样做,并让链接器在其他地方搜索模板实例化:

extern template class std::vector<MyClass>; // 仅限C++0x


4
不一定是内联的。它在编译单元中实例化,可能会被内联,也可能不会被内联。 - Michael Ekstrand
1
export 关键字与内联无关,而是提供编译后的模板,以便客户端代码稍后实例化。 - Edward Strange

0

你的意思是实例化,而不是编译。在编译时,编译器会找出代码使用的每个版本,并实例化(在对象文件中)所有所需的版本。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接