C++ 是如何实现的?链接模板实例
如果我在两个不同翻译单元包含的头文件中定义一个函数(可能是类成员函数,但不是内联函数),则会出现链接错误,因为该函数是多重定义的。模板则不然,因为在编译器解析模板化类型对象的声明之前,它们不是可编译类型。这让我意识到我不知道编译后的模板代码驻留在哪里以及它是如何链接的,因为 C++ 不只是创建多个代码副本来定义 SomeTemplateClass。任何信息将不胜感激。 谢谢!
If I define a function (maybe a class member function but not inlined) in a header file that is included by two different translation units I get a link error since that function is multiply defined. Not so with templates since they are not compilable types until the compiler resolves a declaration of an object of a templatized type. This made me realize I don't know where compiled template code resides and how it is linked since C++ does not just create multiple copies of code to define SomeTemplateClass. Any info would be appreciated.
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
C++ 编译器使用 3 种实现方案:
贪婪实例化,编译器在使用它的每个编译单元中生成实例化,然后链接器丢弃除其中一个之外的所有实例化(这不仅仅是一种代码大小优化) ,它是必需的,以便函数地址、静态变量等都是唯一的)。这是最常见的模型。
查询实例化,其中编译器具有已完成实例化的数据库。当需要实例化时,将检查并更新数据库。我所知道的唯一使用此功能的编译器是 Sun 的编译器,并且默认情况下不再使用它。
迭代实例化,其中实例化由链接器进行(直接进行或通过将它们分配给编译单元,然后重新编译)。这是 CFront 使用的模型(即历史上它是第一个使用的模型),也是使用 EDG 前端的编译器使用的模型(与 CFront 相比有一些优化)。
(请参阅 David Vandevoorde 和 Nicolai Josuttis 编写的《C++ 模板完整指南》。另一个在线参考是 http: //www.bourguet.org/v2/cpplang/export.pdf,更关心编译模型,但仍然有实例化机制的描述)。
There are 3 implementation schemes used by C++ compilers:
greedy instantiation, where the compiler generates an instantiation in each compilation unit that uses it, then the linker throws away all but one of them (this is not just a code-size optimization, it's required so that function addresses,
static
variables, and the like are unique). This is the most common model.queried instantiation, where the compiler has a database of instantiations already done. When an instantiation is needed, the DB is checked and updated. The only compiler I know which uses this is Sun's, and it isn't used by default anymore.
iterated instantiation, where the instantiations are made by the linker (either directly or by assigning them to a compilation unit, which will then be recompiled). This is the model used by CFront -- i.e. historically it was the first one used -- and also by compilers using the EDG front-end (with some optimisations compared to CFront).
(See C++ Templates, The Complete Guide by David Vandevoorde and Nicolai Josuttis. Another online reference is http://www.bourguet.org/v2/cpplang/export.pdf, which is more concerned about the compilation model but still has descriptions of the instantiation mechanisms).
所有模板函数都是隐式内联的。正如类声明中定义的方法是隐式内联的一样。
当我说隐式内联时,我指的是这个词更现代的用法。请参阅我的冗长描述 在这里。
简而言之,
inline
、static
和extern
都是同级链接指令。 inline 告诉链接器忽略函数的重复定义。一般来说,这意味着链接器将选择一个定义并将其用于所有编译单元。我不知道有哪个编译器会在最终的可执行文件中保留所有重复的模板代码。模板实例存储在哪里?
它们以与内联函数相同的方式存储在同一位置。其详细信息是特定于编译器的。
All template functions are implicitly inline. Just as methods defined in the class declaration are implicitly inline.
When I say implicitly inline I mean the more modern usage of the word. See my lengthy description here.
In short,
inline
,static
, andextern
are all sibling linkage directives. inline tells the linker to ignore duplicate definitions of a function. Generally this means the linker will pick one definition and use it for all compilation units. I don't know of any compilers that do or did leave all duplicate template code in the final executable.Where are template instantiations stored?
They are stored in the same way in the same place as inline functions. The details of that are compiler specific.
这是特定于实现的。
一些编译器会为实例化的每个翻译单元一遍又一遍地生成相同的模板实例,并让链接器折叠重复项。
当链接器尚未完成该任务时,模板就会因“代码膨胀”而名声不佳。如今,这可能是不值得的。当某些实现编译为相同的目标机器代码时,它们甚至会折叠不同的实例。 (如
f()
和f()
,因为指针类型只是生成的机器代码中的地址。)其他人会推迟模板编译直到链接时为止,并且可能还有其他方法来处理这个问题。正如我所说,这取决于实施。
这些都有不同的优点和缺点。如果没有真正的模块概念,我怀疑有人会想出完美的方案。
对于
导出
,过去需要编译器预编译模板代码并根据请求实例化。然而,除了一个供应商之外,没有人为其编译器实现export
,现在它已被删除。This is implementation specific.
Some compilers will generate the same template instances over and over for each translation unit they are instantiated in and let the linker fold the duplicates.
Templates got a bad reputation for "code bloat" when linkers weren't yet up to that task. Nowadays this is probably undeserved. Some implementations will even fold different instantiations when they compile to the same target machine code. (Like
f<A*>()
andf<B*>()
, as pointer types are just addresses in the generated machine code.)Others will defer template compilation until link-time instead, and there might be still other ways to deal with this. As I said, it's up to the implementation.
These all have different advantages and disadvantages. Absent of a true module concept I doubt anyone will come up with the perfect scheme.
With
export
there used to be a requirement for compilers to pre-compile template code and instantiate at request. However, except for one vendor nobody implementedexport
for their compiler, and now it's removed.它实际上确实创建了多个副本。这些副本很特殊,不违反单一定义规则。一些链接器会出现,删除副本,并使用它们重新链接函数;并非所有人都这样做。
It actually does create multiple copies. Those copies are special and don't violate the one-definition rule. Some linkers will come along, remove the copies, and relink the functions using them; not all do.