模板代码在使用模板函数时才会被编译。但是编译后的代码保存在哪里呢?它保存在调用该模板函数的对象文件中吗?
例如,main.cpp从文件test.h调用一个模板函数,编译器生成一个对象文件main.o, 模板函数是否在main.o文件中?因为模板代码没有被内联,对吗?
模板代码在使用模板函数时才会被编译。但是编译后的代码保存在哪里呢?它保存在调用该模板函数的对象文件中吗?
例如,main.cpp从文件test.h调用一个模板函数,编译器生成一个对象文件main.o, 模板函数是否在main.o文件中?因为模板代码没有被内联,对吗?
这完全取决于编译器的实现。大多数编译器将在类似cpp文件中生成代码以进行内联编译。有时,通过优化设置,一些编译器甚至会重用相同的代码而不是为每个cpp重新创建它。
因此,您需要查看编译器的文档以了解更多详细信息。
main.o
文件中被输出。 它的一些部分可能会被内联,就像其他任何代码一样可能被内联,但通常情况下,模板代码会被输出到任何实例化该模板的文件中。可以将其视为首先实例化模板代码以生成普通的非模板函数,然后使用编译器应用于任何其他函数的内联和优化来编译这些函数。std ::vector<int>
)出现在多个编译单元(.cpp
文件) 中时,会存在一些困难,因为代码在每个编译单元中都会实例化。 解决这种情况的方法有很多种,有时涉及到链接阶段的清理步骤,其中重复的模板实例化被解析为单个实例化;您的编译器手册可以提供更多关于它如何处理这种情况的信息。即使未实例化,模板代码也会被编译。否则,编译器不需要对此发出诊断:
template< typename T >
void f()
{
blah
}
模板编译分为两个阶段。除了基本检查外,所有依赖于模板参数的内容只能在实例化模板并使用实际类型填充形式参数时进行检查。例如,在这里。
template< typename T >
void f()
{
typename T::nested_type x;
}
T::nested_type
只能在模板实例化并为 T
给定实际类型后进行检查。然而,当编译器遇到模板定义时,会执行基本检查(“给定 T::nested_type
是一种类型,这是一个有效的变量定义吗?”)。这就是为什么需要 typename
的原因。根据 T
,T::nested_type
可能是 T
的成员名称或静态数据成员的名称,这将使 T::nested_type x;
成为语法错误。因此,我们必须告诉编译器将 T::nested_type
视为类型名称。
它总是内联的(意思是,它总是具有内联语义的内部链接)。实际上,就像内联函数一样,它可能根本没有被内联,但模板不是代码。它是“生成代码的模板”。因此,除了特殊情况,它通常会驻留在头文件中,详见下文。
曾经有一个想法,叫做“导出关键字”,用于制作其他东西。但它已从标准中删除。
特殊情况:您可以将模板实例化编译成对象文件,而无需使用它们。这是避免所有模板代码内联的唯一方法。操作方式如下:
template class std::vector<MyClass>;
这将强制编译器在当前位置实例化模板。C++0x将有一种语法来强制编译器不这样做,并让链接器在其他地方搜索模板实例化:
extern template class std::vector<MyClass>; // 仅限C++0x
你的意思是实例化,而不是编译。在编译时,编译器会找出代码使用的每个版本,并实例化(在对象文件中)所有所需的版本。