导出模板是2011年之前的C ++中的一项功能,其中模板的实现将推迟到单独的源文件中。
According to this article,可以写"手册"通过手动指定实现文件中使用的每种类型来导出模板。因为这只会创建特定的符号 - 我相信与重载函数类似的方式 - 如何实现它是有道理的。
但我理解导出的模板(通过export
关键字)是任意的。它可以允许传递任何类型的数据。
举个简单的例子:
template<typename T>
T add(T value1, T value2) {
return value1 + value2;
}
如果T为int
,float
或特别是std::string
(以及任何其他超出+
的类型,则此示例的程序集输出会有很大差异操作者)。
由于它完全是任意的,编译器如何实现这个关键字?
我的猜测是在链接时生成代码,这很可能需要一个自定义目标文件格式,包含代码的一些表示。但这也使链接器成为编译器,从而打破了preprocesser-compiler-linker分离。
答案 0 :(得分:1)
2001年左右,EDG实施了export
,而Comeau为我提供了早期版本。我确实能够实例化A<B<A<B<A<int> > > > >
,其中A<T>
在A.cpp中定义,B<T>
在B.cpp中定义。显然,这需要某种形式的链接时代码生成。
这更令人惊讶,因为Comeau实际上使用了MSVC作为后端,而微软同时认为这是不可能的! (这就是我首先评估export
的原因,WG21论文N1426)
答案 1 :(得分:0)
模板化事物只有在被编译的代码引用时才会实例化为具体类型。
此时编译器需要所有信息(结构定义,函数定义)才能生成代码。
如果编译器在导入结构时看到的唯一定义是
export templat< ... > class foo;
它没有生成代码的机制,并且在请求类foo的功能时失败。
相反,在构建库时,编译器可以使用定义和实现,但是它无法看到库可以使用哪些类型。
可用的唯一(需要?)机制是头文件。这有定义和实现,并且不需要特殊的对象格式。
如果您明确地实例化模板的具体示例,则可以从库中导出这些示例,但仅适用于预先知道的类型。
答案 2 :(得分:0)
有一个神话,某些人期望导出的模板严格地单独编译,但没有证据表明其中的任何人(如果存在)都是委员会成员。
导出的模板确实是具有普通模板语义的普通模板。 (它们不是类似的O'Caml或Haskell模块。)
生成二进制代码的单独编译是基于ABI的 。为功能模板(相对于其实例化)使用ABI毫无意义。
C ++中的模板本质上类似于宏。并非基于令牌的C / C ++预处理程序宏类,因此它们无视C或C ++语法结构,但它们非常接近于基于替换的类(在语法实体上不是词法分析器杠杆)。
他们倾向于从实例化上下文中提取很多名称,这比我们想要的更多。那是因为在C ++中没有办法拥有“概念”元类型系统。 (而且C ++太复杂了,太不规则了,无法改装这样的东西。)
没有C ++模板的“概念”约定,C ++语言定义就不能严格限制在实例化上下文中可能要查找的名称:即使是a<b
这样的显而易见的定义也没有C ++中的布尔值。按照惯例,它是一个由普通布尔类型(bool
或int
表示的布尔值,但可以是任何东西(存在使返回类型成为类类型的用例)。
因此,与f(a<b)
不同,即使模板中的f
之类的东西也可能需要从实例化上下文中提取f(bool(a<b))
。
显然不可能单独进行模板的单独编译。从来没有导出模板的意图。
基于模板定义上下文和实例化上下文上下文来实例化模板。如果模板不是头文件,则仅表示编译器将考虑两个源文件。因此,这甚至不是一个严重的困难。 (当然有困难的问题,但是处理多个源文件不是一个。)
当然,这意味着C ++代码的“链接”将不是正常的链接:它将涉及为每个所需的模板实例化再次运行编译器,直到完成所有实例化。
(并且“运行时链接”将不包括此类模板-实例化链接,并且将不允许调用编译器来创建代码。)
我在这里提到的所有内容当时都很清晰明了。几乎没有证据表明,任何为语言的设计做出重大贡献的人都有不同的直觉。
实际上,最初C ++仅导出了模板,以至于它没有export
关键字。