【问题标题】:about template code orginisation: where to put code that a template uses关于模板代码组织:将模板使用的代码放在哪里
【发布时间】:2013-11-14 21:45:36
【问题描述】:

我知道模板定义应该放在头文件中。这是否意味着模板使用(直接或间接)的所有类的定义也需要放在头文件中?

我有一个模板,它有很多依赖的类,因此必须将它们全部放在头文件中,否则我会得到“错误 LNK2019:未解析的外部符号”。在代码组织方面有没有更好的解决方案?

例子:

    double inline MainFunction(double price, const Params& params)
    {
        Price<ModeEnum::NORMAL> pricer(price);
        MethodOne<ModeEnum::NORMAL> methodOne;
        return pricer.func(methodOne, params) ;       
    }


template<ModelEnum::Enum Mode>
struct Price
{
    double price;
    typedef double return_type;
    Price(double price_) : price(price_){}

    template<typename T> double func(const T& method, const Params& params) const
    {
        const typename T::PriceFactor factor(params);
        return factor ..... ;
    }
};

T::PriceFactor 实际上是 B 类,它是在模板 MethodOne 中定义的类型定义。因此,我必须将 B 类的构造函数以及它使用的所有(很多)函数和类放在头文件中。

【问题讨论】:

  • 您仍然可以使用#include 来分隔文件之间的各种类。你能举个小例子,只是用虚拟的类名吗?
  • Forward declare 尽你所能。
  • 好吧。这是模板。所以我猜身体也很重要

标签: c++ templates header


【解决方案1】:

简单的答案是:当模板被实例化时,所有代码都需要对编译器可见。如果代码不可见,编译器将不会进行实例化,您需要提供显式实例化。显式实例化是否可行,取决于模板的性质:

  1. 适用于多种类型的模板,例如,std::vector&lt;T&gt; 之类的模板可能希望完全在标头中实现。您可以将函数模板的声明和定义分开,但将这些部分放在不同的文件中没有多大意义。
  2. 适用于少数类型的模板,例如,std::basic_ostream&lt;cT&gt;charwchar_t 实例化,并且可能在某些时候用char16_tchar32_t 进行实例化,可能希望在标题中声明并定义在另一个 not 自动包含的标题中。相反,带有定义的标头仅包含在显式实例化类模板的特殊实例化文件中。
  3. 某些模板为具有不同属性的类提供相同的接口。 std::complex&lt;T&gt; 曾经是这种情况,可以用floatdoublelong double 实例化。对于这样的模板,标题只会包含声明,而定义将进入合适的翻译单元。
  4. 与上述讨论正交的一个主题是将公共部分分解出来,理想情况下,将非模板分解为具有较少实例化的模板:很可能采用一个非常通用的接口,但在实现它作为模板实现的一部分,以某种方式弥合差距为代价的更具限制性的接口。在这种情况下,“有趣”的实现可能会进入源文件而不是头文件,并且接口中的模板只是将传入的类型调整为实际的实现。

当上面提到代码将被放入源文件时,显然只适用于非平凡代码:出于性能原因,简单的转发函数可能应该保留inline 函数。但是,这些往往不是有趣的函数模板,会导致大量依赖。

有关如何组织模板代码的更完整的文章,请参阅blog entry

【讨论】:

    【解决方案2】:

    如果它很简单,我只是把它们放在一个标题中:

    //simple_template.h
    #ifndef SIMPLE_TEMPLATE_H
    #define SIMPLE_TEMPLATE_H
    template <typename T>
    class SomethingSimple
    {
    public:
      T foo() { return T();}
    };
    #endif
    

    如果更复杂,我创建一个“内联标题”(并使用 google 样式指南中的命名约定)来获取:

    //complicated_template.h
    #ifndef COMPLICATED_TEMPLATE_H
    #define COMPLICATED_TEMPLATE_H
    template <typename T>
    class SomethingComplicated
    {
    public:
      T foo();
    };
    #include "compilcated_template-inl.h"
    #endif
    
    //compilcated_template-inl.h
    #ifndef COMPLICATED_TEMPLATE_INL_H
    #define COMPLICATED_TEMPLATE_INL_H
    #include "complicated_template.h"
    template <typename T>
    T SomethingComplicated<T>::foo() {/*lots of code here*/; return T();}
    #endif
    

    这样,complex_template.h 可读性很好,但是任何使用模板的人都可以只包含该标题。例如,

    //uses_template.h
    #ifndef USES_TEMPLATE_H
    #define USES_TEMPLATE_H
    #include "complicated_template.h"
    class something_using_complicated
    {
    private:
       SomethingComplicated<int> something_;
    };
    

    注意:如果使用模板的类也是模板类,那么您将被困在仅标题库中。这就是为什么 BOOST 主要是 headers。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多