在模板头文件末尾包含.cpp文件

9
我正在阅读一本较旧的数据结构书籍,它说当你进行模板类编程时,应该在.h文件末尾包含.cpp文件。就我所知,对于任何模板类成员函数,你都必须在.h文件中完整实现函数 - 这是由于模板编译器的工作方式所致。我所学过的唯一可以放在模板类实现文件中的函数是模板特化函数,即:template<> Class::function_name()。为什么这本书要建议在.h文件末尾包含.cpp文件呢?这只是一种将实现分开到不同文件中并使它们与头文件一起编译的方法吗?如果是这样,那么真正的特化应该放在哪里呢?我猜它们不能放在被头文件包含的.cpp文件中。
5个回答

11

很可能作者更喜欢将声明和定义放在不同的文件中,我猜这是因为这样更容易在声明和定义之间跳转。

但是使用 "cpp" 文件扩展名有点令人困惑。通常这些文件被称为 "ipp",代表 "Inline C++"。


3
我这辈子从未见过这些文件被命名为“ipp”。 - John Dibling
@JohnDibling:这都是关于boost的。甚至在SO上也曾经提出过这方面的问题。 - user405725
在C++ Boost库中,为什么有一些头文件的扩展名是.ipp? - Benedikt Waldvogel

3

这可能是一种老掉牙的说法,我认为你对实现和声明分离的分析是正确的。当这本书被写作时,作者可能认为cpp文件是定义所在的文件,而h文件是声明所在的文件。将真正的显式特化放入前者的文件中自然会对链接器造成致命(或至少是无用)的影响,因为会有重复的定义。现在,我会避免将定义文件命名为.cpp。


2
首先,你并不一定非要把模板的实现放在你的.h(或者.hpp或者.hh)文件中;事实上,对于任何不是最简单的模板,我都建议不要这么做。无论哪种情况下,你都需要包含实现。作者想要表达的可能是把实现放到.cpp文件中,并在文件中进行引用。我建议你使用一个不同的名称,因为大多数人(和一些IDE)会认为你应该编译所有的.cpp文件。在源文件和头文件通常是.cc.hh扩展名的情况下,一个常见的约定是将模板实现命名为.tcc;在一个Windows世界中(几乎普遍使用.cpp),我建议使用.tpp等类似格式的扩展名。
请注意,最早的模板实现要求实现代码放在.cpp文件中。然而,这些实现不需要(也不允许)被包含进来;编译器搜索与模板类定义或函数声明相对应的.hpp(或.hh)文件所在的.cpp(或.cc)文件,并生成一个包含它的虚拟源文件(和其他必要的文件)。

0

如果您想使用模板,则整个模板定义必须在实例化模板的TU中可见。因此,您可以将完整的定义放在头文件中。

只有当您真正希望保持类定义和类成员函数体“分离”时,才可能会尝试将那些函数定义放入单独的文件中。但是,上述规则仍然适用,因此您必须将单独的文件与头文件一起包含,以便任何人都能够使用该模板。

这是一个品味问题。如果成员函数体很短,您也可以将它们定义为内联。还要记住,定义在类定义内部的函数被隐式声明为inline,而如果您单独编写函数体,则必须明确指定。

部分专门化的模板仍然是模板,因此同样适用相同的规则。

完全专门化的类模板只是普通类,因此您可以像对待普通类一样处理它:

// FooInt.hpp
template <typename> class Foo;
template <> class Foo<int>
{
  // your class here
};

你可能想到的一件事是“显式模板实例化”,比如template class Foo<int>;。但那是另外一回事;如果你有兴趣使用它,请留言评论。

0
据我所知,对于任何模板类成员函数,您必须在.h文件中进行完整的函数实现-这是由于模板编译器的工作方式所决定的。
也许这本书假设您在.cpp文件中有完整的函数实现。
如果完整的特化在cpp文件中,则不应将其包含在头文件中。如果这样做,很可能无法编译。因为编译器将看到同一函数的多个定义,而头文件通常会被包含在多个源文件中。

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