我知道在最初的C++ 0x标准中有一个叫做的功能export
.
但是我找不到这个功能的描述或解释.该怎么办?另外:哪个编译器支持它?
虽然标准C++没有这样的要求,但是一些编译器要求所有函数模板都需要在它所使用的每个翻译单元中可用.实际上,对于那些编译器,模板函数的主体必须在头文件中可用. .重复:这意味着那些编译器不允许在非头文件中定义它们,例如.cpp文件.为了澄清,在C++中,这意味着:
// ORIGINAL version of xyz.h templatestruct xyz { xyz(); ~xyz(); };
对这些ctor和dtors的定义不满意:
// ORIGINAL version of xyz.cpp #include "xyz.h" templatexyz ::xyz() {} template xyz ::~xyz() {}
因为使用它:
// main.cpp #include "xyz.h" int main() { xyzxyzint; return 0; }
会产生错误.例如,使用Comeau C++,你会得到:
C:\export>como xyz.cpp main.cpp C++'ing xyz.cpp... Comeau C/C++ 4.3.4.1 (May 29 2004 23:08:11) for MS_WINDOWS_x86 Copyright 1988-2004 Comeau Computing. All rights reserved. MODE:non-strict warnings microsoft C++ C++'ing main.cpp... Comeau C/C++ 4.3.4.1 (May 29 2004 23:08:11) for MS_WINDOWS_x86 Copyright 1988-2004 Comeau Computing. All rights reserved. MODE:non-strict warnings microsoft C++ main.obj : error LNK2001: unresolved external symbol xyz::~xyz () [with T1=int] main.obj : error LNK2019: unresolved external symbol xyz ::xyz () [with T1=int] referenced in function _main aout.exe : fatal error LNK1120: 2 unresolved externals
因为在xyz.cpp中没有使用ctor或dtor,因此,没有需要从那里发生的实例化.无论好坏,这就是模板的工作方式.
解决此问题的一种方法是xyz
在此示例中明确请求实例化xyz
.在一个强力的努力中,这可以通过在它的末尾添加这一行添加到xyz.cpp:
template xyz;
它要求(全部)xyz
实例化.但这有点在错误的地方,因为这意味着每次都会出现一个新的xyz类型,必须修改实现文件xyz.cpp.避免该文件的一种较少侵入性的方法是创建另一个:
// xyztir.cpp #include "xyz.cpp" // .cpp file!!!, not .h file!! template xyz;
这仍然有些痛苦,因为每次出现新的xyz时,它仍然需要手动干预.在一个非平凡的计划中,这可能是一个不合理的维护需求.
所以相反,另一种方法是#include "xyz.cpp"
进入xyz.h:
// xyz.h // ... previous content of xyz.h ... #include "xyz.cpp"
你当然可以将xyz.cpp的内容直接带到(剪切并粘贴)到xyz.h的末尾,从而摆脱xyz.cpp; 这是文件组织的问题,最终预处理的结果将是相同的,因为ctor和dtor主体将在头部中,因此被带入任何编译请求,因为那将使用相应的头部.无论哪种方式,这都有副作用,现在每个模板都在您的头文件中.它可能会减慢编译速度,并可能导致代码膨胀.接近后者的一种方法是声明有问题的函数,在本例中为ctor和dtor,如同内联,因此这将要求您在运行的示例中修改xyz.cpp.
顺便说一下,一些编译器还要求在类中内联定义某些函数,而不是在一个函数之外,因此在这些编译器的情况下需要进一步调整上面的设置.请注意,这是编译器问题,而不是标准C++之一,因此并非所有编译器都需要此问题.例如,Comeau C++没有,也不应该.有关我们当前设置的详细信息,请访问http://www.comeaucomputing.com/4.0/docs/userman/ati.html.简而言之,Comeau C++支持许多模型,包括一个接近导出关键字的意图(作为扩展)以及甚至支持导出本身的模型.
最后,请注意C++导出关键字旨在缓解原始问题.但是,目前Comeau C++是唯一被公开以支持导出的编译器.有关详细信息,请参阅http://www.comeaucomputing.com/4.0/docs/userman/export.html和http://www.comeaucomputing.com/4.3.0/minor/win95+/43stuff.txt.希望当其他编译器符合标准C++时,这种情况会发生变化.在上面的示例中,使用export意味着返回生成链接器错误的原始代码并进行更改:使用export关键字在xyz.h中声明模板:
// xyz.h export // ... ORIGINAL contents of xyz.h ...
xyz.cpp中的ctor和dtor将仅通过#includeing xyz.h导出,它已经完成了.因此,在这种情况下,您不需要xyztir.cpp,也不需要xyz.cpp末尾的实例化请求,并且您不需要手动将ctor或dtor引入xyz.h. 使用前面显示的命令行,编译器可能会自动为您完成所有操作.
请参阅此说明以了解其用法
相当多的编译器不支持它,因为它太新了或者在gcc的情况下 - 因为它们不合适.
这篇文章描述了许多编译器的标准支持. Visual Studio支持新的C/C++标准?
请参阅此处和此处了解Herb Sutter对该主题的处理方法.
基本上:导出只在一个编译器中实现 - 在该实现中,导出实际上增加了模板定义和声明之间的耦合,而引入导出的唯一要点是减少这种耦合.
这就是为什么大多数编译器都不打扰的原因.我原本以为他们会从C++ 0x中的语言中删除导出,但我认为他们没有.也许有一天会有一个很好的方法来实现具有预期用途的出口.
导出是一项功能,它在链接器和编译器之间引入了循环依赖关系。正如其他人指出的那样,它允许一个翻译单元包含在另一个翻译单元中使用的模板的定义。链接器将是第一个检测到此错误的链接器,但它需要编译器来实例化模板。这涉及到真正的艰苦工作,例如姓名查找。
Comeau在大约5年前IIRC首次引入它。在我获得的第一个beta版本中,它运行得很好。如果模板A和B来自不同的TU,即使使用A <1>使用B <1>使用A <0>使用B <2>的测试用例,也可以使用。当然,链接器会反复调用编译器,但是所有名称查找都可以正常工作。实例A <1>从A.cpp中找到了在B.cpp中不可见的名称。