请参阅this question about implementing templates.中的第一个答案
具体来说,请注意这句话。
一个常见的解决方案是在头文件中写入模板声明,然后在实现文件(例如,,.tpp)中实现类,并在头的末尾包含这个实现文件。
我大胆地说出了我最感兴趣的部分。
.tpp文件的意义是什么?我试着做了那一页中建议的事情,结果成功了。但后来,我将文件扩展名更改为任意胡言乱语(比如.zz或.ypp),它仍然有效!它应该有效吗?如果它是.tpp或任何其他扩展,有什么关系吗?为什么不使用.cpp呢?
还有一件事我很困惑。
如果我的实现是用.cpp编写的,并且标头定义了非模板函数,那么我只需要编译.cpp文件一次,对吗?至少直到我更改了.cpp文件中的某些内容。
但是,如果我有一个定义模板函数的标头,并且我的实现在一个文件中,并且具有一个随机的奇怪的扩展名,那么它是如何编译的呢?而且,每次我编译#include
的任何源代码时,是否都会编译这些实现呢?
发布于 2017-06-27 07:01:17
如果它是.tpp或任何其他扩展,有什么关系吗?为什么不使用.cpp呢?
扩展名是什么并不重要,但不要使用.cpp
,因为它违背约定(它仍然可以工作,但不要这样做;.cpp
文件通常是源文件)。除此之外,这是你的代码库使用什么的问题。例如,我(和Boost代码库)为此使用了.ipp
。
.tpp文件的意义是什么?
当您不希望包含模块接口的文件包含所有血淋淋的实现细节时,就会使用它。但是您不能在.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
)文件。
发布于 2017-06-27 07:01:36
对预处理程序来说,文件扩展名是没有意义的;.h
也没有什么神圣之处。这只是惯例,所以其他程序员知道并理解文件中包含的内容。
预处理器将允许您将任何文件包含到任何翻译单元(这是一个非常钝的工具)。像这样的扩展只会帮助澄清应该包含在哪里的内容。
发布于 2017-06-27 07:02:29
如果它是.tpp或任何其他扩展,有什么关系吗?为什么不使用.cpp呢?
实际使用哪个扩展并不重要,只要它与用于C++翻译单元的任何标准扩展不同。
其理由是具有不同的文件扩展名,因为任何用于翻译单元的C++构建系统(.cpp
、.cc
、.)通常都会检测到它们。因为将这些文件转换为源文件将失败。它们必须是包含模板声明的相应头文件的#include
d。
但是如果实现文件有一些看起来很奇怪的文件扩展名,那么从编译的角度来看,它是如何工作的呢?
它需要像前面提到的那样被编译为#include
d。
它是否与cpp中的实现一样有效?
嗯,对于编译时间,没有像从翻译单元生成的纯对象文件那样有100%的效率。当包含#include
语句的头部发生变化时,将再次编译它。
而且,每次我编译
#include
的任何源代码时,是否都会编译这些实现呢?
是的,他们是。
https://stackoverflow.com/questions/44774036
复制相似问题