C++ 外部模板&;不完全类型

C++ 外部模板&;不完全类型,c++,templates,c++11,incomplete-type,C++,Templates,C++11,Incomplete Type,最近,当我试图优化include层次结构时,我偶然发现了文件a.hpp: template<class T> class A { using t = typename T::a_t; }; class B; extern template class A<B>; #include <b.hpp> template class A<B>; 这样我就不必从a.hpp中包含b.hpp,这似乎是减少编译时间的好主意。但是它不工作(a.hpp本身

最近,当我试图优化include层次结构时,我偶然发现了文件
a.hpp

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

class B;

extern template class A<B>;
#include <b.hpp>
template class A<B>;
这样我就不必从
a.hpp
中包含
b.hpp
,这似乎是减少编译时间的好主意。但是它不工作(
a.hpp本身不编译!)有更好的方法吗


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

最后一个外部模板类A告诉编译器,在某些编译单元中有这样一个模板专门化的声明。编译器继续运行,然后链接器应该抱怨没有找到正确的类。它不是形式错误的;这取决于用例。您可以在一个单独的cpp文件中定义模板a。如果您反复编译它,这显然只会减少一点编译时间。您可以使用不同的结构:

一个a.hpp,仅包含a类模板

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

b、 cpp包含一个.hpp,在里面你做了一个显式模板实例化模板类a;(与extern无关)

此时,无论何时需要使用该模板,都可以编写

外部模板类A


并链接已编译的b.cpp文件。如果由于仍然需要模板而包含了a.hpp文件,则由于使用了extern命令,因此不会重新编译该文件。

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

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

From

用于声明的显式实例化的外部说明符 类模板仅抑制定义的显式实例化 以前未专门化的成员函数和静态数据成员的 在包含声明的翻译单元中

因此,如果a中需要B的定义,您不能在不了解B的情况下使用
外部模板
。您当然可以尝试摆脱该要求。在给定的情况下,可以使用t
声明删除
,并使用元函数生成该类型:

template<typename T>
struct get_a_t;

template<typename T>
struct get_a_t<A<T>>
{
   using type = typename T::a_t;
};
模板
结构获取;
模板
结构获取
{
使用type=typename T::a\u T;
};

我不确定这在你的情况下是否可行。只要A需要存储A
B
或A
B::A_t
,您就需要
B
。不过,引用和指针也可以。

您使用的是哪种编译器?我尝试了clang 3.6.1和gcc 4.8.2…“它不工作”。请详细说明你的意思。你的意思是它不再编译了吗?或者你的意思是它没有像你希望的那样减少编译时间?我更新了这个问题。“it not work”(它不工作)的意思是,如果源文件,比如说
main.cpp
,只包含
a.hpp
但不包含
b.hpp
然后
main.cpp
不编译,因为类型
b
不完整。您是否建议我将
extern模板类a
a.hpp
移动到
main.cpp
,其中包括
a.hpp
之前的
b.hpp
模板类?是的,你必须把它从那里移走。这取决于谁使用模板。将它放在该文件中,模板将不会重新编译,因为您显式地告诉编译器该类可用并在另一个翻译单元中编译。您必须在另一个文件中显式地实例化该模板,例如,为了引用该标准,您需要大量地实例化该模板。你的元函数解决方案在我的情况下是不可行的;这会使事情变得太复杂。对于我的问题,似乎没有一个很好的解决方案,所以停止使用显式模板实例来处理我上面描述的情况。这会增加编译时间,但会导致更干净的include层次结构。