可能重复:
Why should the implementation and the declaration of a template class be in the same header file?
例如,在定义模板类时,为什么类方法的实现需要在头文件中?为什么它们不能在实现文件中(cpp / cxx)?
答案 0 :(得分:12)
模板类不是类,它是可用于创建类的模板。当您实例化这样的类时,例如MyTemplate<int>
,编译器在现场创建类。为了创建它,它必须查看所有模板化的成员函数(以便它可以使用模板来创建实际的成员函数,例如MyTemplate<int>::foo()
),因此这些模板化的成员函数必须位于标题中。
如果成员不在标题中,编译器将简单地假设它们存在于其他地方并且只是从模板化函数声明创建实际函数声明,这会给你链接器错误。
“export”关键字应该可以解决这个问题,但很少有编译器支持它(我只知道Comeau)。
您还可以显式实例化MyTemplate<int>
- 然后编译器在编译包含MyTemplate<int>
成员函数定义模板的cpp文件时将为MyTemplate
创建实际的成员函数。
答案 1 :(得分:4)
在实例化它们时,它们需要对编译器可见。这基本上意味着,如果您要在标题中发布模板,那么如果您依赖于隐式实例化,那么包含该标题的所有翻译单元都必须能够看到这些定义。
如果要显式实例化模板,则无需在标题中定义它们,但在大多数情况下这不是一个好主意。
至于原因,它基本上归结为当编译器解析定义时不编译模板的事实,而是在实例化它们时,然后针对特定的实例化类型编译它们。
答案 2 :(得分:2)
如果您的编译器支持export
,那么它不支持export
。只有基于EDG的编译器才支持template<typename T>
struct X {
T t;
X(int i): t(i) {}
};
,因此它将从C ++ 0x中删除。
非导出模板要求编译器可以看到完整的模板定义,以便为您提供的特定类型实例化它们作为参数。例如:
X<float>(5)
现在,当您在某个翻译单元中编写X<float>(5)
时,编译器作为编译该翻译单元的一部分必须检查X的构造函数是否类型正确,生成代码它,等等。因此,它必须看到X的定义,以便允许X<char*>(5)
但禁止export
。
确保编译器在使用它的所有翻译单元中看到相同模板定义的唯一合理方法是将定义放在头文件中。但就标准而言,欢迎您手动复制粘贴,或在cpp文件中定义模板,该模板仅在该一个翻译单元中使用。
{{1}}实际上告诉编译器它必须将解析后的模板定义形式输出到特殊类型的目标文件中。然后链接器执行模板实例化。使用普通的工具链,编译器足够智能以执行模板实例化,而链接器则不然。请记住,模板实例化必须完成除了基本解析之外编译器所做的所有。
答案 3 :(得分:0)
他们可以在CPP文件中。
问题源于编译器基于每个翻译单元为模板类的特定实例化(例如std :: vector&lt; int&gt;)构建代码。在CPP文件中定义函数的问题是您需要在该CPP文件中定义每个可能的表单(这称为模板特化)。
因此,对于上面的这个int vector,你可以在CPP文件中为使用特化的int case定义一个函数。
e.g
template<> void std::vector< int >::push_back( int& intVal )
当然,这样做可以为特定情况带来优化优势,但它确实让您了解STL可以引入多少代码膨胀!至少所有函数都没有像某个编译器那样定义为内联;)
答案 4 :(得分:0)
模板的这个方面称为编译模型,不要与作为How does C++ link template instances主题的实例化机制相混淆。
实例化机制是“何时生成实例化?”这一问题的答案,实例化模型是“找到源的位置?”的答案。
有两种标准编译模型:
包含,您知道的那个,定义必须在哪里,
分离,允许在关键字export
的帮助下将定义放在其他位置。该标准已从标准中删除,在C ++ 0X中不可用。其中一个被删除的理由是它没有得到广泛实施(只有一个实施)。
参见C ++模板,David Vandevoorde和Nicolai Josuttis的完整指南或http://www.bourguet.org/v2/cpplang/export.pdf获取更多信息,分离的编译模型是后来论文的主题。