【问题标题】:extern template & incomplete types外部模板和不完整类型
【发布时间】:2016-04-11 23:16:47
【问题描述】:

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

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

class B;

extern template class A<B>;

这似乎是不正确的。实际上,似乎最后的 extern 模板语句会导致 A&lt;B&gt; 的实例化,这会导致编译器抱怨类型不完整。

我的目标是在a.cpp 中定义A&lt;B&gt;

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

这样我就不必从a.hpp 中包含b.hpp,这似乎是减少编译时间的好主意。但是它不起作用(a.hpp 本身无法编译!)有没有更好的方法来做到这一点?

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

【问题讨论】:

  • 您使用的是哪个编译器?
  • 我试过 clang 3.6.1 和 gcc 4.8.2...
  • “它不起作用”。请详细说明您的意思。你的意思是它不再编译?还是您的意思是它并没有像您希望的那样减少编译时间?
  • 我更新了问题。 “它不起作用”是指如果一个源文件,比如main.cpp,只包含a.hpp,但不包含b.hpp,那么main.cpp 不会编译,因为B 类型不完整。

标签: c++ templates c++11 incomplete-type


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

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

    因此,如果 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。不过,引用和指针都可以。

    【讨论】:

    • 非常感谢您参考标准。在我的情况下,您使用元功能的解决方案是不可行的;它会使事情复杂化太多。我的问题似乎没有一个很好的解决方案,因此对于我上面描述的情况停止使用显式模板实例。这会增加编译时间,但会导致包含层次结构更加清晰。
    【解决方案3】:

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

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

    一个带有 B 类的 b.cpp 文件及其 .h 文件。 (是模板吗?)

    b.cpp 包含 a.hpp 并在其中创建一个明确的模板实例化模板类 A; (不与外部)。

    此时,只要您需要使用该模板,您就可以编写

    外部模板类A;

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

    【讨论】:

    • 您是否建议我将extern template class A&lt;B&gt;a.hpp 移动到main.cpp,其中包括b.hpp 之前的a.hpp
    • 也是一个模板类吗?是的,你必须从那里移动它。这取决于谁使用 A 模板。将它放在该文件中,模板将不会被重新编译,因为您明确告诉编译器该类可用并在另一个翻译单元中编译。您必须在另一个文件中显式实例化模板,例如
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-25
    • 2011-12-14
    • 2021-12-11
    • 2014-05-11
    • 1970-01-01
    相关资源
    最近更新 更多