0

I understand that template definitions should be put in the header file. Does this mean that all the definitions of the classes that the template uses (directly or indirectly) need to be put in the header files as well?

I have a template that has a lot of classes it depends on and thus have to put them all in the header file otherwise I will get "error LNK2019: unresolved external symbol ". Is there a better solution in terms of code organisation?

Example:

    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 is actually class B that is a type definition defined in the tempalte MethodOne. Because of this, I have to put the constructor of class B and all (a lot) the functions and class that it uses in the header file.

Peaceful
  • 472
  • 5
  • 13
  • You can still use `#include` to separate various classes between files. Can you give a small example, just with dummy class names? – pippin1289 Nov 14 '13 at 21:54
  • [Forward declare](http://stackoverflow.com/questions/4757565/c-forward-declaration) anything you can. –  Nov 14 '13 at 21:55
  • well. this is tempalte. So I guess the body is also very important – Peaceful Nov 14 '13 at 22:08

2 Answers2

2

The simple answer is this: all code needs to be visible to the compiler when the template gets instantiated. If the code isn't visible, the compiler won't do the instantiation and you'll need to provide an explicit instantiation. Whether an explicit instantiation is viable, depends on the nature of your template:

  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. Some templates give classes with different properties the same interface. That used to be the case with std::complex<T> which could be instantiated with float, double, and long double. For templates like these the header would only include the declarations and the definitions would go into a suitable translation unit.
  4. One theme which is orthogonal to the above discussion is factoring out common parts, ideally into non-templates are into templates with fewer instantiations: it may very well be possible to take a very general interface but implement it in terms of a much more restrictive interface at the cost of somehow bridging the gap as part of the template implementation. In that case the "interesting" implementation may go into a source file rather than a header and the templates in the interface just adapt the passed in types to the actual implementation.

When mentioning above that code would be place into a source file this, obviously only applies to non-trivial code: simple forwarding functions probably should stay inline functions for performance reasons. However, these tend not to be the interesting function templates causing lots of dependencies.

For a more complete write-up on how to organize template code see this blog entry.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
0

If its simple, I just put it all in one header:

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

If it is more complicated, I create an "inline header" (and use the naming convention from the google style guide) to get:

//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

This way, complicated_template.h is pretty readable, but anyone who uses the template can just include that header. For example,

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

Note: if the classes that use the template are also template classes then you're stuck with a header only library. This is why BOOST is mostly headers.

IdeaHat
  • 7,641
  • 1
  • 22
  • 53