【问题标题】:C++ templates declare in .h, define in .hppC++ 模板在 .h 中声明,在 .hpp 中定义
【发布时间】:2010-08-19 21:14:09
【问题描述】:

我看到一些代码,其中开发人员在 .h 文件中定义了一个类模板,并在 .hpp 文件中定义了它的方法。这让我有点意外。

在处理模板时,C++ 中是否有特定的约定以及它们应该在哪些文件中?

例如,假设我有一个 Vector 类模板,其中包含用于向量运算(加、减、点等)的方法。如果模板参数是float(比较运算符),我还想专门化某些函数。您将如何在文件之间分隔所有这些(指定是否为 .h、.hpp、.cpp)。

【问题讨论】:

    标签: c++ templates header


    【解决方案1】:

    通常(根据我的经验,YMMV)hpp 文件是#include-ed CPP 文件。这样做是为了将代码分成两个物理文件,一个主包含文件和一个实现细节文件,您的库的用户不需要知道这些文件。这样做是这样的:

    super_lib.h(您的客户需要的唯一文件#include

    template<...> class MyGizmo
    {
    public:
      void my_fancy_function();
    };
    
    #include "super_lib_implementation.hpp"
    

    super_lib_implementation.hpp(你的客户不要直接#include这个)

    template<...> void MyGizmo<...>::my_fancy_function()
    {
     // magic happens
    }
    

    【讨论】:

    • 我过去做过这个,我相信 boost 做了很多。
    • 感谢您解释推理。不过,我不认为我会以这种方式使用它。
    • @rhubarb:如果您正在寻找实现模板的其他选项,您可能会发现这很有趣:stackoverflow.com/questions/3040480/…
    • @rhubarb 你偷了我的名字! :-)
    【解决方案2】:

    这对我来说听起来很不寻常。模板的定义和所有特化必须与其声明一起编译,export 模板除外,该功能实际上不存在。

    C++0x 确实引入了extern 模板声明,它允许您在不同的源文件(翻译单元)中定义明确的特化。这已经作为 GCC 和其他平台的扩展存在。

    拆分成两个文件可能有助于稍微“隐藏”实现,或者允许对 doxygen 进行某种惰性。

    啊哈!也可能使用预编译的头文件来改善编译时间。编译器可以在每个文件的基础上缓存标头。然后可以修改单独的“实现”标题而不接触“接口”标题。但反之亦然,实现头文件仍将占用大部分编译时间,而且收益将非常脆弱,并且取决于平台和所做的特定更改。最后,PCH 改进了几个源文件的时间,优化头依赖是没有意义的。

    【讨论】:

    • 不同意“傻”。如果模板库很复杂或有许多功能,从可读性的角度来看,将实现拆分为 hpp 文件很有意义。
    • @John:很好,“不寻常”。但是,从中间拆分文件并加入#include,您可以获得多少可读性?可读性来自于将原型声明放在顶部,而不是拆分文件。
    • 有时很多。我同意同样可以通过将实现推到同一个文件的末尾来完成,但我个人更喜欢尽可能只将用户需要知道的内容放在头文件中。添加更多的东西变得混乱。不过,我认为这里没有正确或错误的意见。
    【解决方案3】:

    在我看来,这是一种令人困惑的代码分离方式。 .h 代表 header.hpp 代表 C++ header。将模板定义放入.hpp 而其他代码放入.h 似乎滥用文件扩展名。

    模板代码通常与模板声明一起写在一个标头中,或者在另一个标头中,也可以像.tcc之类的特殊后缀,然后包含在放入模板声明的标头中。但实际上,文件扩展名无关紧要,只要您在项目中保持一致即可。

    例外情况是您使用显式实例化,并且确切地知道您将需要哪些实例化。想象一下,您有一个模板,以及它的两个实例:

    template<typename T>
    struct SymbolTable {
      T *lookup();
      // ...
    };
    
    template struct SymbolTable<GlobalSym>;
    template struct SymbolTable<LocalSym>;
    

    你不需要将lookup等的定义放到header中。您可以将它们全部放入.cpp 文件中,以及两个显式实例化指令。

    您问的最后一点是关于另一个主题:显式专业化。我建议您对此提出单独的问题。简而言之,所有模板参数都具有具体值/类型的显式特化定义应放入.cpp 文件中,但需要将它们的声明放入标题中(以告诉其他人这些特定成员是特化的)。

    【讨论】:

    • 我从未见过hpp 用于除#include-ed 实现文件之外的任何东西。不同的笔画,我想。就个人而言,我使用inc 来实现这样的目的。
    • @John all 提升标头以.hpp 结尾,包括cstdint.hpp。事实上,在我的 boost 头目录中,只有 8 个 .h 文件(一些 C python 的东西等),但有 7074 个 .hpp 文件。
    • @litb:很公平。诚然,我并没有经常使用 boost,而且我忘记了 boost 以这种方式使用hpp
    【解决方案4】:

    我从未听说过将类声明放在.h 中,将模板定义放在.hpp 中。我见过的每个项目都采用了.h.hpp 的意思相同的方法,你应该标准化一个(通常是.h)。

    模板方法可以放在.h 文件的末尾,也可以放在单独的-inl.h 文件中(如Google C++ Style Guide 所建议的那样)。

    【讨论】:

    • 它们也可以放在.hpp 文件中。事实上,它们可以放在.super_ball 文件、.i_like_pizza 文件或任何其他类型的文件中。
    • @Potatoswatter 通过提出更好的替代方案以及您的理由来使您的评论具有建设性如何?
    【解决方案5】:

    我认为分离接口 (.h) 和实现 (.hpp) 文件的一个方面是模板的用户(即另一个开发人员)只需查看 .h 文件即可了解如何使用模板,而不会被其实际实现分心。

    【讨论】:

    • 接口和实现分离是个好主意,但是可以通过将接口放在顶部来在一个文件中完成。
    【解决方案6】:

    如果我们在 .h 文件中定义类模板,并在 .hpp 文件中定义其方法,那么我们必须在 .h 文件中 #include .hpp 文件。在 .h 文件的末尾简单地定义方法更容易(然后不需要 .hpp 文件)。

    【讨论】:

    • 不正确。您只需要在 .cpp 文件的某处#include .hpp 文件。
    【解决方案7】:

    Boost 库对包含声明和实现的标头使用“.hpp”。在这种情况下,无需链接(预编译)库。

    就 Boost 思想而言,“.inl”之类的扩展名可能是描述模板声明头的“实现”源文件的好主意。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-06
      • 1970-01-01
      • 2011-01-09
      • 1970-01-01
      • 1970-01-01
      • 2018-01-10
      相关资源
      最近更新 更多