用于实例化模板代码的显式习惯用法 - 不包括其源代码

Posted

技术标签:

【中文标题】用于实例化模板代码的显式习惯用法 - 不包括其源代码【英文标题】:An explicit idiom for instantiating templated code - without including its source 【发布时间】:2013-11-16 14:37:25 【问题描述】:

在 C 和 C++ 中,函数、ADT 和类的一个非常常见的编码模式是:

带有(类函数的)声明的头 .h 文件。 包含实际代码的实现 .cpp 文件。

这些被编译成一个单独的对象(共享与否)。其他代码,使用声明的实体(我们称之为“foo”),包括 foo.h 文件,单独编译,然后链接到 foo.o。

但是,对于模板化的 foo,这是不可能的:如果不指定所需的实例化类型,foo.o 是无用的。每个人似乎都在做的是将实现代码(通常是 foo.cpp)包含在与用户代码相同的翻译单元中。

我希望能够避免这种情况,使用一些不需要包括 foo.cpp 的机制。理想情况下,这应该有效:

main.cpp:

#include "foo.h"
int main() 
    foo<int>();

foo.h:

template<typename T> void foo();

foo.cpp:

template<typename T> void foo() 
    // implementation here

为此,我想我需要某种巧妙的习语,可能涉及我的构建机制,而不仅仅是源文件和头文件,因此我不需要包含实现代码,只需包含头文件。我在想可能是实现文件的一些内容,包括一些自动生成的标题,例如

template foo<int>();

这将解析它们缺少的实例的目标文件(即两次编译传递),或者可能解析源文件(调整编译器?启用一些辅助输出?);你能推荐这样一个成语吗?还是我没有考虑过的另一种选择?

注意:当然,重点是 foo 模块(foo.hfoo.cpp)的代码并不“知道”需要哪些实例化,例如不知道main()会用foo&lt;int&gt;()还是foo&lt;unsigned char&gt;()

【问题讨论】:

C++ 标准曾经有一个特性(我认为 export 关键字是为此保留的),但 AFAIK 唯一或多或少正确支持此特性的编译器是 SGI 的 C++ 编译器。此功能后来被删除。 @Axel - EDG(爱迪生设计集团)实施了export,然后建议将其从语言中删除。 那么SGI编译器有EDG的前端吗?因为我记得使用它(一定是在 1997/8 左右)并且不耐烦地等待它被 gcc 实现,只是后来才知道它被丢弃了。 包含源代码绝对没有任何问题。只需将其放在 .h 文件中即可。 @n.m.:我没有说这是“错误的”。但我想封装我的实现。我不想用实现文件中的各种垃圾污染我主要的翻译单元。 【参考方案1】:

实际上,仅用于实例化,我们甚至不必复制签名:

template decltype(foo<int>) foo;

这在这个例子中并没有太大的区别,但是如果你的签名很长而且很乏味的话。

【讨论】:

【参考方案2】:

这是显式实例化。

template foo<int>();

它告诉编译器让fooint 实现。就像您在 .cpp 文件中为 int 编写实现一样。如果您知道将来将使用哪些类型,这将很有用。如果您在许多翻译单元中使用foo&lt;int&gt;,它可以减少项目中的总编译时间。例如:

文件FooBar.h

template <typename T>
class Bar

public:
    T data;
    void func();
;

template<typename T> void foo();

文件FooBar.cpp

template <typename T>
void Bar<T>::func()



template <typename T>
void foo() 


// explicit instantiation

template class Bar<int>;
template class Bar<std::string>;
template foo<int>();
template foo<float>();

主要问题是,您的 fooBar 作为模板化的东西只能用于实例化类型。例如,foo 限制为 intfloatBar 限制为 intstd::string

【讨论】:

+1 是的,但我认为您应该就如何使用它提供建议(即将实现放入 .cpp 文件并添加所有需要的显式实例化)。 @MM。 - 你忘了提到这种方法是多么痛苦,这就是为什么没有人再这样做了。 &lt;g&gt; @PeteBecker:我在最后添加了一个声明,检查它是否是您想要的。谢谢。 对不起,我的幽默没有通过。我没有什么特别的想法。问题是它是维护的噩梦,因为每次使用模板时,您都必须检查(通过查看实例文件或编译以查看是否有错误)相应的实例化是否在实例文件中。这意味着使用的每个模板,包括您的模板使用的模板。 我想我的问题一定是不够清楚。我建议我可以使用显式实例化,但重点是 foo.h 和 foo.cpp 不必知道哪些类型是必要的——它们需要忘记 main.cpp 需要 foo&lt;int&gt;() 而不是foo&lt;long long&gt;()。也许我应该再澄清一下。

以上是关于用于实例化模板代码的显式习惯用法 - 不包括其源代码的主要内容,如果未能解决你的问题,请参考以下文章

正确使用函数的显式模板实例化?

仅标头模板的显式实例化声明(外部模板)

将 pimpl 与 Templated Class 和显式实例化的模板一起使用

如何对复杂的模板化函数进行显式实例化?和相关的 Intellisense 错误

g ++中的显式模板专业化导致麻烦

智能转换与 KOTLIN 中的显式转换有何不同