【问题标题】:Should I include everything I need in a template class header file?我应该在模板类头文件中包含我需要的所有内容吗?
【发布时间】:2014-10-17 19:03:58
【问题描述】:

当我编写一个类 toString 的通用模板类时,我遇到了一个难题,它给出了一个代表我使用的常见数据结构的 QString。我希望我可以在此头文件 (#include in .h or .c / .cpp?) 中包含最少数量的其他头文件,但也希望它不依赖于包含的顺序 (Header file order)。

基本代码是这样的:

// Some headers

template <typename T>
class PMsg
{
    // Public routines
    public:
        static QString fromType();
        static QString fromValue(T const &val);
}; // PMsg
// Inline definition of all methods in PMsg<T>::fromValue
// Some specialisation
// To be continued...

问题来了:容器类。我是否应该执行以下操作来拉动所有容器标头以使其工作:

// To the beginning
#include <QtCore/QList>
#include <QtCore/QVector>

// After PMsg<T>
template <typename T>
class PMsg<QList<T> >
{
    // Public routines
    public:
        static QString fromType();
        static QString fromValue(QList<T> const &val);
}; // PMsg
// Some definitions

template <typename T>
class PMsg<QVector<T> >
{
    // Public routines
    public:
        static QString fromType();
        static QString fromValue(QVector<T> const &val);
}; // PMsg
// Some definitions

或者使用宏来检测包含哪些标头:

#if defined(QLIST_H)
template <typename T>
class PMsg<QList<T> >
{
    // Public routines
    public:
        static QString fromType();
        static QString fromValue(QList<T> const &val);
}; // PMsg
// Some definitions
#endif

#if defined(QVECTOR_H)
template <typename T>
class PMsg<QVector<T> >
{
    // Public routines
    public:
        static QString fromType();
        static QString fromValue(QVector<T> const &val);
}; // PMsg
// Some definitions
#endif

我最终可能会包含 20 多个不同的标头(包括一些非 Qt 类,例如 Eigen 中的类),或者我可能会依赖 Qt 可以更改的东西,而无需告诉下游开发人员并要求我的同事在此标头之前包含 Qt 标头当然。

我也不确定此时两种方式对编译时间的影响有多大(如果我没有解决这个问题,可能不应该考虑)。

有没有其他更好的方法来解决这个问题或者我应该遵循哪种方法?

抱歉问题标题不好,感谢您的帮助。

【问题讨论】:

  • 库提供者负责在其标头中包含保护;不是你。包括编译成功所需的内容。您不能(也不应该)假设第 3 方库的 include-guard 宏的名称,更不用说在 您的代码 中对它们进行硬编码了。而 none 是编译时的;它是管理这些的 preprocessor 步骤,并且如果由包含的头文件正确实现,将对您的最终编译时间产生近乎零的影响。只需包含第一个示例中的标题即可。让他们的 include-guards 和预处理器来解决它。
  • 您的第二个链接问题是第一个答案的问题的副本,该问题列出了通常用来帮助整理不正确的依赖假设的口头禅。 See this answer.
  • 只要未定义主模板(非专业化),您可能需要考虑为每个专业化单独的头文件。
  • @WhozCraig,感谢您的帮助。我同意你的观点,我永远不应该依赖我无法控制的东西。在发布此问题之前,我阅读了该答案。它没有解决但您解决的问题之一是是否适合依赖警卫检测和包含顺序,我的程序实际上可以使用它。
  • @VaughnCato,我想我需要提供更多信息。我们有一个名为 PLogger 的下游类,它使用模板方法调用 PMsg::fromValue() 并写下消息。如果我做对了,我需要该类中包含的所有专业化,这是 PMsg 类的主要用法。我怀疑分离专业是否会有所帮助。

标签: c++ qt header-files


【解决方案1】:

根据您的描述,您将提供一个带有自定义点 (PMsg&lt;T&gt;) 的组件,用于添加的组件。您似乎还希望拥有一组已知的组件来利用此自定义。这导致以下推理:

  1. 要使用您的组件,必须包含一个特定的标头(您没有命名它,所以我将使用"pmsg.h")。
  2. 新创建的组件可以并且可能应该在其各自的标头中提供相关的自定义,即无需在 "pmsg.h" 中自定义这些组件。
  3. 现有组件(例如QVector&lt;T&gt;)不知道这种自定义,并且要与您的组件无缝集成,它们的自定义需要在"pmsg.h" 中声明。如果没有在"pmsg.h" 中声明自定义,所有用户都需要记住除了现有组件的标头之外还包括声明自定义的标头。假设有一个自定义的默认实现,这不仅会产生意想不到的结果,而且当不同的翻译单元与其包含的标头不一致时,很容易导致 ODR 违规。
  4. 对于非模板组件,您可以在"pmsg.h" 中转发组件以声明自定义,然后在实现"pmsg.h" 声明的源文件中不包含任何包含的情况下实现它。也就是说,对于自定义的非模板组件,不需要在声明中包含相应的标头,声明就足够了。
  5. 一般来说,您不能安全地前向声明类模板(主要是因为只能为第一个声明声明默认参数)。此外,您可能需要在标题"pmsg.h" 中提供模板化自定义的定义。也就是说,您需要在 "pmsg.h" 中包含您想要/需要自定义的所有模板类的声明。

tl;dr:如果您想为现有组件提供自定义,您需要在标题中声明它们,并且您还需要包含它们各自的声明。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-03
    • 1970-01-01
    • 2015-10-06
    • 2011-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-10
    相关资源
    最近更新 更多