为什么仅在标题文件中实现模板?
引用来自 c ++标准库:教程和手册::
目前使用模板的唯一便携方式是通过使用内联函数在标题文件中实现它们。
为什么这是?
(澄清:标头文件不是唯一的便携式解决方案。但是它们是最方便的便携式解决方案。)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
引用来自 c ++标准库:教程和手册::
目前使用模板的唯一便携方式是通过使用内联函数在标题文件中实现它们。
为什么这是?
(澄清:标头文件不是唯一的便携式解决方案。但是它们是最方便的便携式解决方案。)
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(20)
警告:将实现放入标题文件中是不是,请参见本答案末尾的替代解决方案。
无论如何,您的代码失败的原因是,在实例化模板时,编译器会使用给定的模板参数创建新类。例如:
阅读此行时,编译器将创建一个新类(我们称其为
fooint
),这等同于以下内容:因此,编译器需要访问方法的实现。 ,用模板参数实例化(在这种情况下
int
)。如果这些实现不在标题中,则将无法访问它们,因此编译器将无法实例化模板。一个常见的解决方案是在标题文件中写入模板声明,然后在实现文件中实现类(例如.tpp),然后在标题末尾包含此实现文件。
foo.h
foo.tpp
以这种方式,实施仍然与声明分开,但编译器可以访问。
另一种解决
方案是将实现分开,并明确实例化所需的所有模板实例:
foo.h
foo.cpp
如果我的解释不够清楚,您可以查看 c ++ super-faq在此主题上。
Caveat: It is not necessary to put the implementation in the header file, see the alternative solution at the end of this answer.
Anyway, the reason your code is failing is that, when instantiating a template, the compiler creates a new class with the given template argument. For example:
When reading this line, the compiler will create a new class (let's call it
FooInt
), which is equivalent to the following:Consequently, the compiler needs to have access to the implementation of the methods, to instantiate them with the template argument (in this case
int
). If these implementations were not in the header, they wouldn't be accessible, and therefore the compiler wouldn't be able to instantiate the template.A common solution to this is to write the template declaration in a header file, then implement the class in an implementation file (for example .tpp), and include this implementation file at the end of the header.
Foo.h
Foo.tpp
This way, implementation is still separated from declaration, but is accessible to the compiler.
Alternative solution
Another solution is to keep the implementation separated, and explicitly instantiate all the template instances you'll need:
Foo.h
Foo.cpp
If my explanation isn't clear enough, you can have a look at the C++ Super-FAQ on this subject.
这是因为需要单独的汇编和模板是实例化式多态性。
让我们更接近混凝土以进行解释。说我有以下文件:
类myclass< t>
的接口
类myclass< t>
foo.cpp类myclass< t>
的实现
类myclass< t>
bar.cppmyclass< int
单独的汇编意味着我应该能够独立于 bar.cpp 独立于 foo.cpp 。编译器在每个编译单元上完全独立地完成了分析,优化和代码生成的所有艰苦工作;我们不需要进行全程分析。只有链接器才需要一次处理整个程序,并且链接器的作业非常容易。
bar.cpp 当我编译 foo.cpp 时,甚至不需要存在,但是我仍然应该能够链接 foo.o.o 我已经和 bar.o 我只是刚刚制作的,而无需重新编译 foo.cpp 。 foo.cpp 甚至可以将其编译到动态库中,分布在没有 foo.cpp 的其他地方,并与我写 foo.cpp后写几年的代码联系。
“实例化式多态性”是指模板
myclass< t>
并不是真正可以将可以编译到代码的通用类,该代码可以适用于任何值的> t
。这将添加诸如拳击之类的开销,需要将功能指针传递给分配器和构造函数等。C++模板的意图是避免编写几乎相同的class myclass_int
,class myclass_float 等,但仍然能够最终得到编译的代码,就像我们 分别编写了每个版本一样。因此,模板是实际上模板;类模板是不是类,它是为我们遇到的每个
t
创建新类的食谱。不能将模板编译到代码中,只能编译实例化模板的结果。因此,当编译 foo.cpp 时,编译器看不到 bar.cpp 知道需要
myclass< int>
。它可以看到模板myClass< t>
,但它不能为此发射代码(这是模板,而不是类)。当编译 bar.cpp 时,编译器可以看到它需要创建一个myClass< int>
,但是它看不到模板myClass< t>
(仅其接口 foo.h ),因此无法创建它。如果 foo.cpp 本身使用
myclass< int
,则将在编译 foo.cpp 时生成代码,因此,当时Bar.o 与 foo.o 相关联,它们可以连接并可以工作。我们可以使用该事实来通过编写单个模板在.cpp文件中实现有限的模板实例。但是, bar.cpp 无法将模板用作模板并在其喜欢的任何类型上实例化;它只能使用 foo.cpp 的作者认为提供的模板类的先前版本。您可能会认为,在编译模板时,编译器应“生成所有版本”,而这些版本在链接过程中从未被过滤出来。除了巨大的开销和极端困难之外,这种方法还会面临,因为“类型修饰”功能(例如指针和阵列)甚至允许仅内置类型产生无限数量的类型,当我现在扩展程序时会发生什么通过添加:
类Bazprivate
,并使用myClass< bazprivate>
否则不可能使用这种方法
myclass< t>
的新新颖实例化,myclass< t>
的完整模板,以便编译器可以在 myClass< bazprivate> > baz.cpp 。没有人喜欢(1),因为全程分析汇编系统将 forever 进行编译,并且因为它使得没有源代码的编译库不可能。因此,我们有(2)。
It's because of the requirement for separate compilation and because templates are instantiation-style polymorphism.
Lets get a little closer to concrete for an explanation. Say I've got the following files:
class MyClass<T>
class MyClass<T>
MyClass<int>
Separate compilation means I should be able to compile foo.cpp independently from bar.cpp. The compiler does all the hard work of analysis, optimization, and code generation on each compilation unit completely independently; we don't need to do whole-program analysis. It's only the linker that needs to handle the entire program at once, and the linker's job is substantially easier.
bar.cpp doesn't even need to exist when I compile foo.cpp, but I should still be able to link the foo.o I already had together with the bar.o I've only just produced, without needing to recompile foo.cpp. foo.cpp could even be compiled into a dynamic library, distributed somewhere else without foo.cpp, and linked with code they write years after I wrote foo.cpp.
"Instantiation-style polymorphism" means that the template
MyClass<T>
isn't really a generic class that can be compiled to code that can work for any value ofT
. That would add overhead such as boxing, needing to pass function pointers to allocators and constructors, etc. The intention of C++ templates is to avoid having to write nearly identicalclass MyClass_int
,class MyClass_float
, etc, but to still be able to end up with compiled code that is mostly as if we had written each version separately. So a template is literally a template; a class template is not a class, it's a recipe for creating a new class for eachT
we encounter. A template cannot be compiled into code, only the result of instantiating the template can be compiled.So when foo.cpp is compiled, the compiler can't see bar.cpp to know that
MyClass<int>
is needed. It can see the templateMyClass<T>
, but it can't emit code for that (it's a template, not a class). And when bar.cpp is compiled, the compiler can see that it needs to create aMyClass<int>
, but it can't see the templateMyClass<T>
(only its interface in foo.h) so it can't create it.If foo.cpp itself uses
MyClass<int>
, then code for that will be generated while compiling foo.cpp, so when bar.o is linked to foo.o they can be hooked up and will work. We can use that fact to allow a finite set of template instantiations to be implemented in a .cpp file by writing a single template. But there's no way for bar.cpp to use the template as a template and instantiate it on whatever types it likes; it can only use pre-existing versions of the templated class that the author of foo.cpp thought to provide.You might think that when compiling a template the compiler should "generate all versions", with the ones that are never used being filtered out during linking. Aside from the huge overhead and the extreme difficulties such an approach would face because "type modifier" features like pointers and arrays allow even just the built-in types to give rise to an infinite number of types, what happens when I now extend my program by adding:
class BazPrivate
, and usesMyClass<BazPrivate>
There is no possible way that this could work unless we either
MyClass<T>
MyClass<T>
, so that the compiler can generateMyClass<BazPrivate>
during compilation of baz.cpp.Nobody likes (1), because whole-program-analysis compilation systems take forever to compile , and because it makes it impossible to distribute compiled libraries without the source code. So we have (2) instead.
这里有很多正确的答案,但是我想添加此(以进行完整):
如果您在实现CPP文件的底部,请明确对模板将使用的所有类型的实例化,则链接器将能够找到它们照常。
编辑:添加显式模板实例化的示例。定义了模板后使用,并定义了所有成员函数。
这将实例化(从而向链接器提供)类及其所有成员功能(仅)。类似的语法适用于功能模板,因此,如果您的非成员运算符过载,则可能需要为这些操作员进行相同的操作。
上面的示例是没有用的,因为向量已在标题中完全定义,除非一个共同的包含文件(预编译标头?)使用
extern模板类class vector&lt; int&gt;
,以防止其实例化。使用向量的其他(1000?)文件。Plenty correct answers here, but I wanted to add this (for completeness):
If you, at the bottom of the implementation cpp file, do explicit instantiation of all the types the template will be used with, the linker will be able to find them as usual.
Edit: Adding example of explicit template instantiation. Used after the template has been defined, and all member functions has been defined.
This will instantiate (and thus make available to the linker) the class and all its member functions (only). Similar syntax works for function templates, so if you have non-member operator overloads you may need to do the same for those.
The above example is fairly useless since vector is fully defined in headers, except when a common include file (precompiled header?) uses
extern template class vector<int>
so as to keep it from instantiating it in all the other (1000?) files that use vector.在将它们实际编译为对象代码之前,需要由编译器实例化模板。只有知道模板参数已知,才能实现这种实例化。现在想象一个场景,其中在
ah
中声明模板功能,在a.cpp
中定义,并在b.cpp
中使用。当编译A.CPP
时,不一定知道即将进行的汇编b.cpp
将需要模板的实例,更不用说哪个特定实例是。对于更多标题和源文件,情况很快就会变得更加复杂。可以说,可以使编译器更聪明地“向前看”模板的所有用途,但是我敢肯定,创建递归或其他复杂的情况并不困难。 Afaik,编译器不会这样做。正如安东指出的那样,一些编译器支持模板实例化的明确导出声明,但并非所有编译器都支持它(尚未?)。
Templates need to be instantiated by the compiler before actually compiling them into object code. This instantiation can only be achieved if the template arguments are known. Now imagine a scenario where a template function is declared in
a.h
, defined ina.cpp
and used inb.cpp
. Whena.cpp
is compiled, it is not necessarily known that the upcoming compilationb.cpp
will require an instance of the template, let alone which specific instance would that be. For more header and source files, the situation can quickly get more complicated.One can argue that compilers can be made smarter to "look ahead" for all uses of the template, but I'm sure that it wouldn't be difficult to create recursive or otherwise complicated scenarios. AFAIK, compilers don't do such look aheads. As Anton pointed out, some compilers support explicit export declarations of template instantiations, but not all compilers support it (yet?).
实际上,在C ++ 11之前,标准定义
导出
关键字,将使将使在标头文件中声明模板并在其他地方实现模板成为可能。以某种说话的方式。并非真的,作为唯一实现的指出了:没有一个流行的编译器实施此关键字。该功能的唯一实现是在Edison设计组编写的前端中,该组由Comeau C ++编译器使用。所有其他要求您在标题文件中编写模板,因为编译器需要模板定义进行适当的实例化(正如其他人已经指出的那样)。
结果,ISO C ++标准委员会决定删除使用C ++ 11的模板的
导出
功能。Actually, prior to C++11 the standard defined the
export
keyword that would make it possible to declare templates in a header file and implement them elsewhere. In a manner of speaking. Not really, as the only ones who ever implemented that feature pointed out:None of the popular compilers implemented this keyword. The only implementation of the feature was in the frontend written by the Edison Design Group, which is used by the Comeau C++ compiler. All others required you to write templates in header files, because the compiler needs the template definition for proper instantiation (as others pointed out already).
As a result, the ISO C++ standard committee decided to remove the
export
feature of templates with C++11.尽管标准C ++没有此类要求,但一些编译器要求在使用它们使用的每个翻译单元中都需要提供所有功能和类模板。实际上,对于这些编译器,必须在标题文件中提供模板功能的物体。重复:这意味着这些编译器不允许在非头部文件(例如.cpp文件)中定义它们,
那里有一个 enfort 关键字,应该减轻此问题,但它远不近地是便携式的。
Although standard C++ has no such requirement, some compilers require that all function and class templates need to be made available in every translation unit they are used. In effect, for those compilers, the bodies of template functions must be made available in a header file. To repeat: that means those compilers won't allow them to be defined in non-header files such as .cpp files
There is an export keyword which is supposed to mitigate this problem, but it's nowhere close to being portable.
模板通常在标题中使用,因为编译器需要根据模板参数给出/推论的参数实例化代码的不同版本,并且更容易(作为程序员)让编译器多次重新编译相同的代码并稍后进行。 。
请记住,模板不能直接表示代码,而是该代码多个版本的模板。
当您在
.cpp
文件中编译非模板函数时,您正在编译混凝土函数/类。模板不是这种情况,可以用不同类型实例化,即,在用混凝土类型替换模板参数时,必须发出具体代码。
导出
关键字有一个功能,该功能用于单独编译。导出
功能在c ++ 11
和afaik中弃用,只有一个编译器实现了它。您不应使用
导出
。在
c ++
或c ++ 11
中不可能单独汇编,但也许在c ++ 17
中,如果概念进入,我们可以拥有某种单独汇编的方式。为了实现单独的汇编,必须进行单独的模板身体检查。
看来可以解决概念的解决方案。
看看这个最近呈现在
标准委员会会议。
我认为这不是唯一的要求,因为您仍然需要在用户代码中实例化模板代码的代码。
我猜想模板的单独汇编问题也是一个问题,该问题正在迁移到模块,这是当前正在使用的问题。
编辑:截至2020年8月起,模块已经成为C ++的现实: https://en.cppreference .com/w/cpp/语言/模块
Templates are often used in headers because the compiler needs to instantiate different versions of the code, depending on the parameters given/deduced for template parameters, and it's easier (as a programmer) to let the compiler recompile the same code multiple times and deduplicate later.
Remember that a template doesn't represent code directly, but a template for several versions of that code.
When you compile a non-template function in a
.cpp
file, you are compiling a concrete function/class.This is not the case for templates, which can be instantiated with different types, namely, concrete code must be emitted when replacing template parameters with concrete types.
There was a feature with the
export
keyword that was meant to be used for separate compilation.The
export
feature is deprecated inC++11
and, AFAIK, only one compiler implemented it.You shouldn't make use of
export
.Separate compilation is not possible in
C++
orC++11
but maybe inC++17
, if concepts make it in, we could have some way of separate compilation.For separate compilation to be achieved, separate template body checking must be possible.
It seems that a solution is possible with concepts.
Take a look at this paper recently presented at the
standards committee meeting.
I think this is not the only requirement, since you still need to instantiate code for the template code in user code.
The separate compilation problem for templates I guess it's also a problem that is arising with the migration to modules, which is currently being worked.
EDIT: As of August 2020 Modules are already a reality for C++: https://en.cppreference.com/w/cpp/language/modules
即使上面有很多很好的解释,我还是缺少一种将模板分为标题和身体的实用方法。
我主要关注的是,当我更改其定义时,避免重新编译所有模板用户。
对于我来说,在模板主体中拥有所有模板实例化并不是一个可行的解决方案,因为模板作者可能不知道其使用情况和模板用户是否没有权利进行修改。
我采用了以下方法,该方法也适用于较旧的编译器(GCC 4.3.4,ACC A.03.13)。
对于每个模板使用情况,其自己的标头文件中都有一个Typedef(从UML模型生成)。它的身体包含实例化(最终在末尾链接的库中)。
模板的每个用户都包含该标头文件并使用Typedef。
示意图示例:
myTemplate.h:
myTemplate.cpp:
myInstantiatedTemplate.h:
myInstantiatedTemplate.cpp:
main.cpp:
这样,只有模板实例化才需要重新编译,而不是所有模板用户(和依赖项)。
Even though there are plenty of good explanations above, I'm missing a practical way to separate templates into header and body.
My main concern is avoiding recompilation of all template users, when I change its definition.
Having all template instantiations in the template body is not a viable solution for me, since the template author may not know all if its usage and the template user may not have the right to modify it.
I took the following approach, which works also for older compilers (gcc 4.3.4, aCC A.03.13).
For each template usage there's a typedef in its own header file (generated from the UML model). Its body contains the instantiation (which ends up in a library which is linked in at the end).
Each user of the template includes that header file and uses the typedef.
A schematic example:
MyTemplate.h:
MyTemplate.cpp:
MyInstantiatedTemplate.h:
MyInstantiatedTemplate.cpp:
main.cpp:
This way only the template instantiations will need to be recompiled, not all template users (and dependencies).
这意味着定义模板类实现方法实现的最便携方法是在模板类定义中定义它们。
It means that the most portable way to define method implementations of template classes is to define them inside the template class definition.
在编译步骤中使用模板时,编译器将为每个模板实例化生成代码。
在编译和链接过程中,.CPP文件转换为纯对象或机器代码,其中包含引用或未定义的符号,因为Main.cpp中包含的.H文件尚无实现。这些已准备好与另一个定义模板实现的对象文件链接,因此您拥有完整的A.Out可执行文件。
但是,由于需要在编译步骤中处理模板,以便为您定义的每个模板实例化生成代码,因此,仅将模板与标头文件分开的模板不起作用,因为它们始终是手工和手工的,原因是从字面上看,每个模板实例化是一个全新的类。在常规类中,您可以将.h和.cpp分开,因为.h是该类的蓝图,并且.cpp是原始实现,因此可以定期编译和链接任何实现文件,但是使用模板.h是如何蓝图的蓝图。该类不应看起来对象的外观含义为模板.cpp文件不是类的原始定期实现,而只是类的蓝图,因此无法编译.H模板文件的任何实现,因为您需要一些具体的编译,从这种意义上讲,模板是抽象的。
因此,模板永远不会单独编译,只有在其他源文件中具有混凝土实例化的任何地方才进行编译。但是,具体的实例化需要知道模板文件的实现,因为只需使用.h文件中的混凝土类型修改
typename t
就不会完成工作,因为什么.cpp在那里。要链接,我以后再也找不到它,因为记住模板是抽象的,无法编译,因此我现在被迫立即进行实施,所以我知道要编译和链接,现在我已经实现了它链接到封闭源文件中。基本上,当我实例化模板时,我需要创建一个全新的课程,如果我不知道使用我提供的类型时该类别的样子,除非我注意到编译器的编译器,否模板实现,因此现在编译器可以用我的类型替换t
,并创建一个可以编译和链接的具体类。综上所述,模板是类应该看起来的蓝图,类是对象应该看起来的蓝图。
我无法编译模板与它们的具体实例化分开,因为编译器仅编译至少在C ++中的混凝土类型是纯语言抽象。可以这么说,我们必须删除模板,我们这样做是为了处理它们的混凝土类型,以便我们的模板抽象可以转换为常规类文件,然后可以正常编译。将模板.h文件和模板.cpp文件分开是毫无意义的。这是荒谬的,因为.cpp和.h的分离仅是可以单独编译和单独链接的.cpp的地方,因为我们不能单独编译它们,因为模板是一种抽象,因此我们始终被迫被迫将抽象始终与混凝土实例化的混凝土实例放在一起,其中始终必须了解所使用的类型。
含义
typename t
在编译步骤中替换了get get而不是链接步骤,因此,如果我尝试在没有t
的情况下编译模板,则被替换为完全意义的具体值类型编译器和结果对象代码无法创建,因为它不知道t
是什么。从技术上讲,创建某种功能可以保存模板。CPP文件并在其他来源找到类型时切换出来,我认为标准确实具有关键字
export
允许您将模板放入单独的CPP文件中,但没有多少编译器实际上实现了这一点。只是一个旁注,在为模板类专业时,您可以将标头与实现分开,因为按定义进行专业化意味着我专门针对可以单独编译和链接的混凝土类型。
The compiler will generate code for each template instantiation when you use a template during the compilation step.
In the compilation and linking process .cpp files are converted to pure object or machine code which in them contains references or undefined symbols because the .h files that are included in your main.cpp have no implementation YET. These are ready to be linked with another object file that defines an implementation for your template and thus you have a full a.out executable.
However since templates need to be processed in the compilation step in order to generate code for each template instantiation that you define, so simply compiling a template separate from it's header file won't work because they always go hand and hand, for the very reason that each template instantiation is a whole new class literally. In a regular class you can separate .h and .cpp because .h is a blueprint of that class and the .cpp is the raw implementation so any implementation files can be compiled and linked regularly, however using templates .h is a blueprint of how the class should look not how the object should look meaning a template .cpp file isn't a raw regular implementation of a class, it's simply a blueprint for a class, so any implementation of a .h template file can't be compiled because you need something concrete to compile, templates are abstract in that sense.
Therefore templates are never separately compiled and are only compiled wherever you have a concrete instantiation in some other source file. However, the concrete instantiation needs to know the implementation of the template file, because simply modifying the
typename T
using a concrete type in the .h file is not going to do the job because what .cpp is there to link, I can't find it later on because remember templates are abstract and can't be compiled, so I'm forced to give the implementation right now so I know what to compile and link, and now that I have the implementation it gets linked into the enclosing source file. Basically, the moment I instantiate a template I need to create a whole new class, and I can't do that if I don't know how that class should look like when using the type I provide unless I make notice to the compiler of the template implementation, so now the compiler can replaceT
with my type and create a concrete class that's ready to be compiled and linked.To sum up, templates are blueprints for how classes should look, classes are blueprints for how an object should look.
I can't compile templates separate from their concrete instantiation because the compiler only compiles concrete types, in other words, templates at least in C++, is pure language abstraction. We have to de-abstract templates so to speak, and we do so by giving them a concrete type to deal with so that our template abstraction can transform into a regular class file and in turn, it can be compiled normally. Separating the template .h file and the template .cpp file is meaningless. It is nonsensical because the separation of .cpp and .h only is only where the .cpp can be compiled individually and linked individually, with templates since we can't compile them separately, because templates are an abstraction, therefore we are always forced to put the abstraction always together with the concrete instantiation where the concrete instantiation always has to know about the type being used.
Meaning
typename T
get's replaced during the compilation step not the linking step so if I try to compile a template withoutT
being replaced as a concrete value type that is completely meaningless to the compiler and as a result object code can't be created because it doesn't know whatT
is.It is technically possible to create some sort of functionality that will save the template.cpp file and switch out the types when it finds them in other sources, I think that the standard does have a keyword
export
that will allow you to put templates in a separate cpp file but not that many compilers actually implement this.Just a side note, when making specializations for a template class, you can separate the header from the implementation because a specialization by definition means that I am specializing for a concrete type that can be compiled and linked individually.
如果担心是通过使用它来编译.H作为所有.CPP模块的一部分而产生的额外汇编时间和二进制尺寸膨胀,那么在许多情况下,您可以做的就是使模板类从非策略的基础类中下降。接口的非类型依赖性部分,该基类可以在.cpp文件中实现。
If the concern is the extra compilation time and binary size bloat produced by compiling the .h as part of all the .cpp modules using it, in many cases what you can do is make the template class descend from a non-templatized base class for non type-dependent parts of the interface, and that base class can have its implementation in the .cpp file.
只是为了在这里添加一些值得注意的东西。当不是功能模板时,可以在实现文件中定义模板类的方法。
myqueue.hpp:
myqueue.cpp:
Just to add something noteworthy here. One can define methods of a templated class just fine in the implementation file when they are not function templates.
myQueue.hpp:
myQueue.cpp:
单独实现的一种方法如下。
Inner_foo.h
foo.tpp
foo.h
main.cpp
innion_foo.h
具有正向声明。foo.tpp
具有实现,并包括innion_foo.h
;foo.h
只有一行,包括foo.tpp
。在编译时,将
foo.h
的内容复制到foo.tpp
,然后将整个文件复制到foo.h
之后编译。这样,没有限制,命名是一致的,以换取一个额外的文件。我之所以这样做,是因为代码中断的静态分析仪在
*。TPP
中看不到类的前面声明时。在任何IDE或使用YouCompleteme或其他内容中编写代码时,这很烦人。A way to have separate implementation is as follows.
inner_foo.h
foo.tpp
foo.h
main.cpp
inner_foo.h
has the forward declarations.foo.tpp
has the implementation and includesinner_foo.h
; andfoo.h
will have just one line, to includefoo.tpp
.On compile time, contents of
foo.h
are copied tofoo.tpp
and then the whole file is copied tofoo.h
after which it compiles. This way, there is no limitations, and the naming is consistent, in exchange for one extra file.I do this because static analyzers for the code break when it does not see the forward declarations of class in
*.tpp
. This is annoying when writing code in any IDE or using YouCompleteMe or others.这是完全正确的,因为编译器必须知道其分配类型。因此,如果要公开或库的一部分(静态或动态),则模板类,功能,枚举等也必须在标题文件中实现是。如果编译器不知道该类型是无法编译的。在.NET中,它可以是因为所有对象都来自对象类。这不是.NET。
That is exactly correct because the compiler has to know what type it is for allocation. So template classes, functions, enums,etc.. must be implemented as well in the header file if it is to be made public or part of a library (static or dynamic) because header files are NOT compiled unlike the c/cpp files which are. If the compiler doesn't know the type is can't compile it. In .Net it can because all objects derive from the Object class. This is not .Net.
我建议查看此GCC页面,讨论了模板实例化的“ Cfront”和“ Borland”模型之间的权衡。
https://gcc.gnu.gnu.org.org.org.org/onlinedocs/gcc -4.6.4/gcc/template-instantiation.html
“ Borland”模型对应于作者所建议的内容,提供完整的模板定义,并多次编译事物。
它包含有关使用手动和自动模板实例化的明确建议。例如,“ -repo”选项可用于收集需要实例化的模板。或其他选择是使用“ -fno-implicer-templates”禁用自动模板实例化以强制手动模板实例化。
根据我的经验,我依靠C ++标准库,并为每个编译单元(使用模板库)实例化模板。对于我的大型模板类,我对所需类型进行手动模板实例化。
这是我的方法,因为我提供了一个工作程序,而不是用于其他程序中的模板库。这本书的作者Josuttis的作者在模板库上工作了很多。
如果我真的担心速度,我想我会使用预编译的标头探索
https://gcc.gnu.org.org/onlinedocs/gcc/gcc/precompiled-headers.html
在许多编译器中获得支持。但是,我认为模板标头文件很难预编译标头。
I suggest looking at this gcc page which discusses the tradeoffs between the "cfront" and "borland" model for template instantiations.
https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Template-Instantiation.html
The "borland" model corresponds to what the author suggests, providing the full template definition, and having things compiled multiple times.
It contains explicit recommendations concerning using manual and automatic template instantiation. For example, the "-repo" option can be used to collect templates which need to be instantiated. Or another option is to disable automatic template instantiations using "-fno-implicit-templates" to force manual template instantiation.
In my experience, I rely on the C++ Standard Library and Boost templates being instantiated for each compilation unit (using a template library). For my large template classes, I do manual template instantiation, once, for the types I need.
This is my approach because I am providing a working program, not a template library for use in other programs. The author of the book, Josuttis, works a lot on template libraries.
If I was really worried about speed, I suppose I would explore using Precompiled Headers
https://gcc.gnu.org/onlinedocs/gcc/Precompiled-Headers.html
which is gaining support in many compilers. However, I think precompiled headers would be difficult with template header files.
”此处复制
(从a .cpp 文件中使用我的所有功能,无论它们是模板函数还是常规功能。并且有一种方法可以使用一些基本
#ifndef
魔术。这是您可以做的:main.cpp
myclass.hpp
myclass.cpp
这是预编译器如何看待它。我们有两个
.cpp
文件。myClass.hpp
myClass
不确定,它是myClass.cpp
myClass_functions
myClass_functions
,它是定义的,myClass_functions
,它不myClass.hpp
myclass
not n defined n nmyClass.cpp
myClass.hpp
ye RememyClass
定义了什么都不在内部做任何事情,请返回<<代码> myClass.cppmyClass_functions
,它为(copying here from a closed duplicate)
I prefer to have all of my functions in the
.cpp
file, regardless of whether they are template functions or regular functions. And there is a way to do that with some basic#ifndef
magic. Here's what you can do:main.cpp
myclass.hpp
myclass.cpp
Here's how the precompiler sees it. We have two
.cpp
files.myclass.hpp
MYCLASS
is undefined, and it ismyclass.cpp
MYCLASS_FUNCTIONS
MYCLASS_FUNCTIONS
is defined, it isMYCLASS_FUNCTIONS
is defined, it isn'tmyclass.hpp
MYCLASS
is undefined, and it ismyclass.cpp
myclass.hpp
againMYCLASS
is defined so do nothing inside, return tomyclass.cpp
MYCLASS_FUNCTIONS
is defined, it isMoshe回答的动机来自: https://stackoverflow.com/a/3844448106/6459849
我一边的小贡献有一个扩展示例。假设有一个整体操作核心,它包含一个响应成功率,其中包含通用类型。
响应响应
Operationsuccess.h
用法:
With motivation from Moshe's answer from: https://stackoverflow.com/a/38448106/6459849
A small contribution from my side with an extended example. Let's say there is an overall OperationSuccess and it contains a ResponseSuccess which has a generic type in it.
ResponseSuccess.h
OperationSuccess.h
Usage:
C ++ 20允许
auto
作为函数参数类型,这使缩写功能模板。编译器还需要访问该函数的实现,以便将其实例化。因此,实现应在标头文件中。
使用
auto
参数无法将分离的实现保持在源文件中的替代解决方案。如果需要,auto
参数可以用键入的模板参数替换。C++20 allows
auto
as a function parameter typeThis makes an Abbreviated function template. The compiler also needs to have access to the implementation of the function to instantiate it with the deduced parameter type. Therefore, the implementation should be in a header file.
The alternative solution of keeping the separated implementation in a source file is not possible with
auto
parameters. If this is required,auto
parameters can be replaced with typed template parameters.在标题文件中同时编写声明和定义是一个好主意的另一个原因是可读性。 这样的模板函数
假设Utility.h: and the Utility.cpp中
有 。当您比较没有实现“&lt;”的两个类实例时,它将丢弃编译器错误。
因此,如果将模板声明和定义分开,您将无法仅读取标题文件以查看此模板的ins and outs以便在您自己的类上使用此API,尽管编译器会在此中告诉您需要覆盖哪个操作员的情况。
Another reason that it's a good idea to write both declarations and definitions in header files is for readability. Suppose there's such a template function in Utility.h:
And in the Utility.cpp:
This requires every T class here to implement the less than operator (<). It will throw a compiler error when you compare two class instances that haven't implemented the "<".
Therefore if you separate the template declaration and definition, you won't be able to only read the header file to see the ins and outs of this template in order to use this API on your own classes, though the compiler will tell you in this case about which operator needs to be overridden.
我不得不编写一个模板和此示例对我有用,
这是一个动态数组类的示例。
现在在您内部.template文件,您可以定义您的函数。
I had to write a template class an d this example worked for me
Here is an example of this for a dynamic array class.
Now inside you .template file you define your functions just how you normally would.