外部模板和不完整的类型

extern template & incomplete types

本文关键字:类型 外部      更新时间:2023-10-16

最近当我尝试优化我的包含层次结构时,我偶然发现了文件a.hpp

template<class T>
class A
{
  using t = typename T::a_t;
};
class B;
extern template class A<B>;

这似乎是格式错误的。事实上,似乎最后的 extern 模板语句会导致实例化 A<B>这导致编译器抱怨不完整的类型。

我的目标是在a.cpp中定义A<B>

#include <b.hpp>
template class A<B>;

这样我就不必包含来自a.hppb.hpp,这似乎是减少编译时间的好主意。但是它不起作用(a.hpp本身无法编译!有没有更好的方法

注意:当然,我不能使用显式模板实例化,但这不是我想要的!我想"预编译"A<B>以节省编译时间(如果使用(,但是如果不使用A<B>,我不想在每个使用a.hpp的文件中都包含b.hpp

extern 模板声明阻止成员函数体的实例化,但它强制实例化类定义,因为编译器无论如何都需要它,并且类体需要模板参数的完整定义,因为它访问其成员。恐怕不可能对A<B>的用户隐藏B的身体。

Extern 模板是一种优化,但它不会改变实例化机制的基本工作原理。

来自 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1448.pdf

用于声明显式实例化的 extern 说明符 类模板仅禁止显式实例化定义 成员函数和静态数据成员以前不专门化 在包含声明的翻译单元中。

因此,如果在 A 中需要 B 的定义,则在不知道 B 的情况下不能使用 extern template。您当然可以尝试摆脱该要求。在给定的情况下,您可以删除 using t 声明并有一个元函数来生成该类型:

template<typename T>
struct get_a_t;
template<typename T>
struct get_a_t<A<T>>
{
   using type = typename T::a_t;
};

不确定在您的情况下是否可行。只要 A 需要存储BB::a_t,就需要B。不过,引用和指针是可以的。

最后一个外部模板类 A 告诉编译器,在某个编译单元中存在此类模板专用化的声明。编译器继续,然后链接器应该抱怨找不到正确的类。它不是畸形的;这取决于用例。您可以在单独的 cpp 文件中定义模板 A。如果你一遍又一遍地编译它,这显然只会减少一点编译时间。您可以执行不同的结构:

一个只有 A 类模板的 a.hpp。

一个 B.cpp 文件,其中包含 B 类及其 .h 文件。(是模板吗?

b.cpp 包括 a.hpp,并在内部创建一个显式模板实例化模板类 A;(不是与外部(。

此时,每当您需要使用该模板时,您只需编写即可

外部模板A类;

并链接编译后的 B.cpp 文件。如果包含 a.hpp 文件,因为您仍然需要模板,则不会重新编译它,因为您有 extern 命令。