请参阅this question about implementing templates.
中的第一个回答具体来说,请注意这个引用
一个常见的解决方案是在头文件中编写模板声明,然后在实现文件(,例如.tpp )中实现该类,并在结尾处包含此实现文件。标题。
我加粗了我最感兴趣的部分 .tpp文件的意义是什么?我尝试完全按照该页面中的建议进行操作。但后来,我将文件扩展名更改为任何随机乱码(如.zz或.ypp),它仍然有效!它应该工作吗?是否.tpp或任何其他扩展名是否重要?为什么不使用.cpp?
这是我很困惑的另一件事 如果我的实现是用.cpp编写的,并且头文件定义了非模板化的函数,那么我只需要编译一次.cpp文件,对吧?至少在我改变.cpp文件中的内容之前。
但是如果我有一个定义模板化函数的标题,并且我的实现是在一个带有随机时髦扩展的文件中,那么它是如何编译的?是否每隔时间编译实现我编译任何源代码#include
的标题?
答案 0 :(得分:14)
是否是.tpp或任何其他扩展名是否重要?为什么不使用.cpp?
扩展名是什么并不重要,但不要使用.cpp
,因为它违反了约定(它仍然可以工作,但不要这样做; .cpp
文件通常是源文件)。除此之外,它是您的代码库使用的问题。例如,我(和Boost代码库)为此目的使用.ipp
。
.tpp文件有什么意义?
当您不希望包含模块接口的文件包含所有gory实现细节时,可以使用它。但是你不能在.cpp
文件中编写实现,因为它是一个模板。所以你尽力而为(不考虑明确的实例化等)。例如
<强> Something.hpp 强>
#pragma once
namespace space {
template <typename Type>
class Something {
public:
void some_interface();
};
} // namespace space
#include "Something.ipp"
<强> Something.ipp 强>
#pragma once
namespace space {
template <typename Type>
void Something<Type>::some_interface() {
// the implementation
}
} // namespace space
我认为在头文件中编写定义以及在单独文件中实现的重点是节省编译时间,因此只需编译一次实现,直到做出一些更改
您无法将常规模板代码拆分为实现文件。您需要可见的完整代码才能使用模板,这就是您需要将所有内容放在头文件中的原因。有关详情,请参阅Why can templates only be implemented in the header file?
但是如果实现文件有一些看似时髦的文件扩展名,那么在编译方面它是如何工作的呢?它是否像在cpp中一样高效?
您不编译.tpp
,.ipp
,-inl.h
等文件。它们就像头文件一样,只是它们只包含在其他头文件中。您只编译源(.cpp
,.cc
)文件。
答案 1 :(得分:6)
文件扩展名对预处理程序毫无意义; .h
也没什么神圣的。这只是惯例,所以其他程序员知道并理解文件包含的内容。
预处理器允许您将任何文件包含到任何翻译单元中(这是一个非常生硬的工具)。像这样的扩展只是帮助澄清应该包括在哪里。
答案 2 :(得分:3)
是否是.tpp或任何其他扩展名是否重要?为什么不使用.cpp?
实际使用哪个扩展名并不重要,只要它与用于C ++翻译单元的任何标准扩展名不同。
原因是文件扩展名不同,因为翻译单元(.cpp
,.cc
,...)的任何C ++构建系统通常会检测到它们。因为将这些转换为源文件会失败。它们必须是包含模板声明的相应头文件#include
。
但是如果实现文件有一些看似时髦的文件扩展名,那么在编译方面它是如何工作的呢?
需要#include
d进行编译。
它是否像在cpp中一样高效?
嗯,对于编译时而言,并不像从翻译单元生成的纯对象文件那样高效100%。只要包含#include
语句的标题发生变化,它就会再次编译。
每次编译任何源代码
#include
s表头时都会编译实现吗?
是的,他们是。
答案 3 :(得分:0)
头文件的文件扩展名在 C++ 中无关紧要,即使应该避免使用标准的源文件扩展名,例如 .cpp
。
但是,有既定的约定。这些帮助人类程序员浏览代码。调用模板实现文件 .tpp
是此类约定之一。
尚未有人提到的是,某些外部工具可能依赖此类约定。
例如,我经常使用流行的 grep
替代品,它只允许在给定类型的文件中进行搜索。该程序会将 .tpp
文件识别为 C++,但不会识别 .zz
文件