C++0x标准中export关键字的最佳解释是什么?

30

我知道在最初的C++0x标准中有一种名为export的功能。

但我找不到关于这个功能的描述或解释。它应该是用来做什么的?另外,哪个编译器支持它?

7个回答

28

虽然标准C++没有这样的要求,但是一些编译器要求所有函数模板在使用的每个翻译单元中都需要可用。实际上,对于这些编译器,模板函数的主体必须在头文件中可用。需要重申的是:这意味着这些编译器不允许它们在非头文件(例如.cpp文件)中定义。换句话说,在C++中,这意味着:

// ORIGINAL version of xyz.h
template <typename T>
struct xyz
 {
    xyz();
    ~xyz();
 };

以下这些构造函数和析构函数的定义可能无法满足要求:

// 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时仍需要手动干预。在一个非平凡的程序中,这可能是一个不合理的维护要求。
因此,另一种方法是将"xyz.cpp"包含到xyz.h的末尾:
// xyz.h

// ... previous content of xyz.h ...

#include "xyz.cpp"

当然,您可以将xyz.cpp的内容直接复制粘贴到xyz.h的末尾,从而摆脱xyz.cpp;这是文件组织的问题,在预处理的结果中,构造函数和析构函数的主体将在头文件中,并因此在使用相应头文件的任何编译请求中引入。无论哪种方式,都会导致现在每个模板都存在于您的头文件中。这可能会减慢编译速度并导致代码膨胀。处理后者的一种方法是将问题函数(在此情况下为构造函数和析构函数)声明为内联函数,因此这将要求您修改运行示例中的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.htmlhttp://www.comeaucomputing.com/4.3.0/minor/win95+/43stuff.txt获取一些详细信息。希望随着其他编译器达到标准C ++的兼容性,这种情况将得以改变。在上面的示例中,使用导出关键字意味着返回生成链接器错误的原始代码,并进行更改:在xyz.h中使用导出关键字声明模板。
// xyz.h

export
// ... ORIGINAL contents of xyz.h ...

通过包含xyz.h文件,xyz.cpp中的构造函数和析构函数将被导出。因此,在这种情况下,您不需要xyztir.cpp,也不需要在xyz.cpp末尾进行实例化请求,也不需要手动将构造函数或析构函数带入xyz.h。使用先前显示的命令行,编译器可能会自动为您完成所有操作。


2
请注意Greg Rogers的回答和那里的评论 - export关键字已从标准中删除,并且可能永远不会在另一个编译器中实现。 - Felix Dombek

8

1
它并不是太新,和 C++ 98 标准中的其他特性一样已经有10年历史了!:D 更确切地说,实现它需要重新设计编译器,而他们认为这并不值得。 - KTC
3
这让链接器变得棘手,尤其是如果你想在链接阶段进行大量的聪明的整个程序优化。 - Martin Beckett

7

请参阅此处此处,了解Herb Sutter对该主题的处理。

基本上:只有一个编译器实现了export - 而在该实现中,export实际上增加了模板定义和声明之间的耦合性,而引入export的唯一目的是减少这种耦合。

这就是为什么大多数编译器不费事的原因。我原以为他们会在C++0x中从语言中删除export,但我认为他们没有。也许有一天会有一个好的方法来实现export,以达到预期的用途。


2
Herb Sutter是微软员工。他试图将C++0x中的export删除,但未成功。因此,他的观点可能有些偏见。声明:我在ISO投了反对票。 - MSalters
请评述一下为什么?我所搜索到的所有信息都表明这个功能已经被废弃,无论它是否仍然存在于标准中。 - T.E.D.
2
Sutter 是微软的员工并不影响此事,他只是认为导出功能不太实用,因此建议将其删除。 - KTC
1
@KTC:确定吗?在我手上的最终草稿中,2.12 [lex.key]/1写道:“表3中显示的标识符保留用作关键字(即它们在第7阶段中被无条件地视为关键字),除了在属性令牌(7.6.1)中[注:export关键字未使用,但保留供将来使用。——结束注释]”,这似乎意味着在当前草案中,export 功能已经消失了。此外,请注意,唯一一个支持 export 的编译器实现者也推动将此功能从新标准中移除。 - David Rodríguez - dribeas
2
@David Rodríguez,嗯,当我写下那条评论时,它应该是“功能完整”的。然后他们又开始拆除功能,包括重新讨论和随后删除导出功能。我不会说EDG推动了它的删除,而是他们对此感到满意。链接文章中Herb关于EDG的评论实际上非常有趣。 - KTC
显示剩余2条评论

5
简单来说: export 允许你在编写模板类时将声明(即头文件)与定义(即代码)分开。如果你的编译器不支持 export,那么你需要把声明和定义放在同一个位置。

5
导出(Export)是一种引入链接器和编译器之间循环依赖关系的功能。正如其他人所指出的那样,它允许一个翻译单元包含在另一个翻译单元中使用的模板的定义。链接器将首先检测到这一点,但它需要编译器来实例化模板。这涉及到真正的艰苦工作,比如名称查找。
Comeau最早在大约5年前推出了这个功能。我记得我第一次得到测试版时它运行得非常好。即使像A<2>使用B<2>使用A<1>使用B<1>使用A<0>这样的测试用例,如果模板A和B来自不同的TU,则可以正常工作。当然,链接器一直在重复调用编译器,但所有名称查找都可以正常工作。实例化A<1>时,从A.cpp中找到了在B.cpp中不可见的名称。

2

2

目前(据我所知),唯一支持导出模板的编译器是Comeau、Borland C++ Builder X附带的编译器(但不包括当前版本的C++ Builder)以及英特尔(至少是非官方的,如果不是官方的话,我不确定)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接