关于模板代码orginisation:放置模板使用的代码的位置

时间:2013-11-14 21:45:37

标签: c++ templates header

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

我有一个模板,它依赖于很多类,因此必须将它们全部放在头文件中,否则我将得到“错误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类,它是tempalte MethodOne中定义的类型定义。因此,我必须将类B的构造函数和所有(很多)函数和类放在头文件中。

2 个答案:

答案 0 :(得分:2)

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

  1. 适用于多种类型的模板,例如像std::vector<T>这样的模板可能希望完全在标头中实现。您可以将声明和函数模板的定义分开,但将部分放入不同的文件中没有多大意义。
  2. 适用于少数类型的模板,例如std::basic_ostream<cT>charwchar_t实例化,可能在某些时候char16_tchar32_t可能希望在标题中声明并在另一个标题中定义,该标题自动包含 not 。相反,带有定义的标头仅包含在明确实例化类模板的特殊实例化文件中。
  3. 某些模板为具有不同属性的类提供相同的接口。以前是std::complex<T>的情况,可以使用floatdoublelong double进行实例化。对于像这样的模板,标题只包含声明,定义将进入合适的翻译单元。
  4. 与上述讨论正交的一个主题是将公共部分分解出来,理想情况下将非模板分解为具有较少实例化的模板:很可能采用非常通用的接口但是实现它很多更具限制性的界面,以某种方式弥补差距作为模板实施的一部分。在这种情况下,“有趣”的实现可能会进入源文件而不是标题,接口中的模板只是将传入的类型调整为实际的实现。
  5. 当上面提到代码将放入源文件时,显然只适用于非平凡的代码:出于性能原因,简单的转发函数可能应该保留inline函数。但是,这些往往不是引起大量依赖的有趣函数模板。

    有关如何整理模板代码的更全面的说明,请参阅此blog entry

答案 1 :(得分:0)

如果简单,我只需将其全部放在一个标题中:

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

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

//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主要是标题。