0

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

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

4

2 回答 2

2

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

  1. Templates which are applicable to many types, e.g., something like std::vector<T> probably want to be implemented entirely in a header. You may separate the declaration and the definition of function templates but there isn't much point in putting the parts into different files.
  2. Templates which are applicable to few types, e.g., std::basic_ostream<cT> which is instantiated with char, wchar_t and maybe at some point with char16_t and char32_t probably want to be declared in a header and defined in another header which is not included automatically. Instead the header with the definitions is included only in special instantiation files where the class templates are explicitly instantiated.
  3. 一些模板为具有不同属性的类提供相同的接口。过去是可以用,和std::complex<T>实例化的情况。对于像这样的模板,标题只会包含声明,而定义将进入合适的翻译单元。floatdoublelong double
  4. 与上述讨论正交的一个主题是将公共部分分解出来,理想情况下,非模板是具有较少实例化的模板:很可能采用非常通用的接口,但根据限制性更强的接口来实现它作为模板实现的一部分,以某种方式弥合差距为代价。在这种情况下,“有趣”的实现可能会进入源文件而不是头文件,并且接口中的模板只是将传入的类型调整为实际的实现。

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

有关如何组织模板代码的更完整的文章,请参阅此博客条目

于 2013-11-14T22:34:43.943 回答
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 主要是标题。

于 2013-11-14T21:59:18.643 回答