Why Do C++ Template Definitions Need to Be in the Header

Why can templates only be implemented in the header file?

Caveat: It is not necessary to put the implementation in the header file, see the alternative solution at the end of this answer.

Anyway, the reason your code is failing is that, when instantiating a template, the compiler creates a new class with the given template argument. For example:

template<typename T>
struct Foo
{
T bar;
void doSomething(T param) {/* do stuff using T */}
};

// somewhere in a .cpp
Foo<int> f;

When reading this line, the compiler will create a new class (let's call it FooInt), which is equivalent to the following:

struct FooInt
{
int bar;
void doSomething(int param) {/* do stuff using int */}
}

Consequently, the compiler needs to have access to the implementation of the methods, to instantiate them with the template argument (in this case int). If these implementations were not in the header, they wouldn't be accessible, and therefore the compiler wouldn't be able to instantiate the template.

A common solution to this is to write the template declaration in a header file, then implement the class in an implementation file (for example .tpp), and include this implementation file at the end of the header.

Foo.h

template <typename T>
struct Foo
{
void doSomething(T param);
};

#include "Foo.tpp"

Foo.tpp

template <typename T>
void Foo<T>::doSomething(T param)
{
//implementation
}

This way, implementation is still separated from declaration, but is accessible to the compiler.

Alternative solution

Another solution is to keep the implementation separated, and explicitly instantiate all the template instances you'll need:

Foo.h

// no implementation
template <typename T> struct Foo { ... };

Foo.cpp

// implementation of Foo's methods

// explicit instantiations
template class Foo<int>;
template class Foo<float>;
// You will only be able to use Foo with int or float

If my explanation isn't clear enough, you can have a look at the C++ Super-FAQ on this subject.

Why do C++ template definitions need to be in the header?

A template class is not a class, it's a template that can be used to create a class. When you instantiate such a class, e.g. MyTemplate<int>, the compiler creates the class on the spot. In order to create it, it has to see all the templated member functions (so that it can use the templates to create actual member functions such as MyTemplate<int>::foo() ), and therefore these templated member functions must be in the header.

If the members are not in the header, the compiler will simply assume that they exist somewhere else and just create actual function declarations from the templated function declarations, and this gives you linker errors.

The "export" keyword is supposed to fix this, but few compilers support it (I only know of Comeau).

You can also explicitly instantiate MyTemplate<int> - then the compiler will create actual member functions for MyTemplate<int> when it compiles the cpp files containing the MyTemplate member function definition templates.

Why should the implementation and the declaration of a template class be in the same header file?

The compiler needs to have access to the entire template definition (not just the signature) in order to generate code for each instantiation of the template, so you need to move the definitions of the functions to your header.

For more details read about The Inclusion Model.

c++ template and header files

Headers.

It's because templates are instantiated at compile-time, not link-time, and different translation units (roughly equivalent to your .cpp files) only "know about" each other at link-time. Headers tend to be widely "known about" at compile-time because you #include them in any translation unit that needs them.

Read https://isocpp.org/wiki/faq/templates for more.

C++ template function compiles in header but not implementation

The problem you're having is that the compiler doesn't know which versions of your template to instantiate. When you move the implementation of your function to x.cpp it is in a different translation unit from main.cpp, and main.cpp can't link to a particular instantiation because it doesn't exist in that context. This is a well-known issue with C++ templates. There are a few solutions:

1) Just put the definitions directly in the .h file, as you were doing before. This has pros & cons, including solving the problem (pro), possibly making the code less readable & on some compilers harder to debug (con) and maybe increasing code bloat (con).

2) Put the implementation in x.cpp, and #include "x.cpp" from within x.h. If this seems funky and wrong, just keep in mind that #include does nothing more than read the specified file and compile it as if that file were part of x.cpp In other words, this does exactly what solution #1 does above, but it keeps them in seperate physical files. When doing this kind of thing, it is critical that you not try to compile the #included file on it's own. For this reason, I usually give these kinds of files an hpp extension to distinguish them from h files and from cpp files.

File: dumper2.h

#include <iostream>
#include <string>
#include <vector>

void test();
template <class T> void dumpVector( std::vector<T> v,std::string sep);
#include "dumper2.hpp"

File: dumper2.hpp

template <class T> void dumpVector(std::vector<T> v, std::string sep) {
typename std::vector<T>::iterator vi;

vi = v.begin();
std::cout << *vi;
vi++;
for (;vi<v.end();vi++) {
std::cout << sep << *vi ;
}
std::cout << "\n";
return;

}

3) Since the problem is that a particular instantiation of dumpVector is not known to the translation unit that is trying to use it, you can force a specific instantiation of it in the same translation unit as where the template is defined. Simply by adding this: template void dumpVector<int>(std::vector<int> v, std::string sep); ... to the file where the template is defined. Doing this, you no longer have to #include the hpp file from within the h file:

File: dumper2.h

#include <iostream>
#include <string>
#include <vector>

void test();
template <class T> void dumpVector( std::vector<T> v,std::string sep);

File: dumper2.cpp

template <class T> void dumpVector(std::vector<T> v, std::string sep) {
typename std::vector<T>::iterator vi;

vi = v.begin();
std::cout << *vi;
vi++;
for (;vi<v.end();vi++) {
std::cout << sep << *vi ;
}
std::cout << "\n";
return;
}

template void dumpVector<int>(std::vector<int> v, std::string sep);

By the way, and as a total aside, your template function is taking a vector by-value. You may not want to do this, and pass it by reference or pointer or, better yet, pass iterators instead to avoid making a temporary & copying the whole vector.

Inclusion of header files in case of templates.

Templates don't actually produce any object code at the point where the compiler reads their source code; they're (typically) only "instantiated" when something actually uses the template. So if you define a template function in one source file, and call it from another, the code for the template function doesn't get compiled at all: it's not in the first object file since nothing there needed it, and it's not in the second object file since the compiler didn't have access to the function's definition.

You define template functions in header files so that in each translation unit where something calls the template function, the compiler has access to its code and can compile a copy of it specialized with the appropriate template arguments.

Alternatively, you can use explicit instantiation: you define the template function in a .cpp file, and also tell the compiler exactly which types that it should compile the function for. This is harder to maintain, because you have to keep track of which instantiations are needed by the rest of the program. If something calls foo<float>(), but you've only explicitly instantiated foo<int>() and foo<char>(), you get a missing-symbol error.

You shouldn't #include a .cpp file from another .cpp file. Just put the template function definitions in the header together with their declarations.

avoiding template definition in header file

You can do the following (as it's widely used practice):

A.hpp


class A {

private:

template<int i>
void method();

};

#include "A.tcc"

A.tcc


template<int i>
void A::method() {
// do something with i
}

Note it's important to name the implementation file with a different extension from .cpp actually, because this will confuse most of the standard build system environments (unless you have complete manual selection of translation unit files).


If you want to have specialized implementations for certain values of i, you can provide them as follows:

// Specialization for 1
template<>
void A::method<1>() {
// do something for specialization with 1
}

template<>
void A::method<2>() {
// do something for specialization with 2
}

Storing C++ template function definitions in a .CPP file

The problem you describe can be solved by defining the template in the header, or via the approach you describe above.

I recommend reading the following points from the C++ FAQ Lite:

  • Why can’t I separate the definition of my templates class from its declaration and put it inside a .cpp file?
  • How can I avoid linker errors with my template functions?
  • How does the C++ keyword export help with template linker errors?

They go into a lot of detail about these (and other) template issues.



Related Topics



Leave a reply



Submit