我知道在最初的C ++ 0x标准中有一个名为export
的功能。
但我无法找到此功能的说明或解释。该怎么办?另外:哪个编译器支持它?
答案 0 :(得分:24)
尽管标准C ++没有这样的要求,但是一些编译器要求所有函数模板都需要在它所使用的每个翻译单元中可用。实际上,对于那些编译器,模板函数的主体必须在头文件。重复:这意味着那些编译器不允许在非头文件中定义它们,例如.cpp文件。为了澄清,在C ++中,这意味着:
// ORIGINAL version of xyz.h
template <typename T>
struct xyz
{
xyz();
~xyz();
};
对ctor和dtors的这些定义不满意:
// ORIGINAL version of xyz.cpp
#include "xyz.h"
template <typename T>
xyz<T>::xyz() {}
template <typename T>
xyz<T>::~xyz() {}
因为使用它:
// main.cpp
#include "xyz.h"
int main()
{
xyz<int> xyzint;
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<T1>::~xyz<int>() [with T1=int] main.obj : error LNK2019: unresolved external symbol xyz<T1>::xyz<int>() [with T1=int] referenced in function _main aout.exe : fatal error LNK1120: 2 unresolved externals
因为在xyz.cpp中没有使用ctor或dtor,因此,没有需要从那里进行的实例化。无论好坏,这就是模板的工作方式。
解决此问题的一种方法是在此xyz
示例中明确请求xyz<int>
的实例化。在蛮力的努力中,可以通过在它的末尾添加这一行来将其添加到xyz.cpp中:
template xyz<int>;
请求实例化(全部)xyz<int>
。但这有点在错误的地方,因为这意味着每次都会出现一个新的xyz类型,必须修改实现文件xyz.cpp。避免该文件的一种较少侵入性的方法是创建另一个文件:
// xyztir.cpp
#include "xyz.cpp" // .cpp file!!!, not .h file!!
template xyz<int>;
这仍然有些痛苦,因为每次出现新的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.使用前面显示的命令行,编译器可能会自动为您完成所有操作。
答案 1 :(得分:6)
请参阅this使用说明
相当多的编译器不支持它,因为它太新了或者在gcc的情况下 - 因为它们不合适。
这篇文章描述了许多编译器的标准支持。 Visual Studio support for new C / C++ standards?
答案 2 :(得分:6)
有关Herb Sutter对该主题的处理,请参阅here和here。
基本上:导出仅在one编译器中实现 - 在该实现中,导出实际上增加了模板定义和声明之间的耦合,而引入导出的唯一要点是减少这种耦合。
这就是为什么大多数编译器都不打扰的原因。我原本以为他们会从C ++ 0x中的语言中删除导出,但我认为他们没有。也许有一天会有一个很好的方法来实现具有预期用途的出口。
答案 3 :(得分:5)
导出是一种在链接器和编译器之间引入循环依赖关系的功能。正如其他人所指出的,它允许一个翻译单元包含另一个翻译单元中使用的模板的定义。链接器将是第一个检测到它,但它需要编译器来实例化模板。这涉及到真正的努力,比如名字查找。
Comeau首先介绍它,大约5年前IIRC。它在我得到的第一个beta版本上运行得很好。甚至像A 2那样的测试用例也是如此。使用B 2和B 2。使用A&lt; 1&gt;使用B 1和B 1。如果模板A和B来自不同的TU,则使用A&lt; 0&gt;工作。当然,链接器反复调用编译器,但所有名称查找都正常。实例A&lt; 1&gt;在A.cpp中找到了在B.cpp中不可见的名称。
答案 4 :(得分:4)
简单地说:
export
允许您在编写模板类时将声明(即标题)与定义(即代码)分开。如果编译器不支持export
,则需要将声明和定义放在一个位置。
答案 5 :(得分:2)
答案 6 :(得分:2)
目前唯一支持导出模板的编译器(据我所知)是Comeau,Borland C ++ Builder X附带的编译器,但不是当前的C ++ Builder,以及英特尔(至少非官方,如果没有正式,不确定)。