Storing C++ Template Function Definitions in a .Cpp File

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.

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.

Definition of a template function inside .cpp file is not working even though I am instantiating a dummy object in .cpp file

Your first question was why writing:

template<> class myclass <int>;

causes an undefined reference error. The answer is that template<> declares an explicit specialization: it's saying that there is a special definition of myclass<int>::doSomething that takes the place of the generic definition earlier in the file. However, the definition of the specialization is not provided. Therefore, no definition of myclass<int>::doSomething is emitted when template.cpp is translated.

Your second question was why replacing the explicit instantiation with:

myclass <int> obj;

in template.cpp did not cause myClass<int>::doSomething to be emitted from that translation unit. The answer is twofold:

  1. When the compiler implicitly instantiates myclass<int> when translating template.cpp, it does not implicitly instantiate myclass<int>::doSomething because, at that point, the function's definition is not yet needed.

  2. Even if the compiler were to implicitly instantiate myclass<int>::doSomething during the translation of template.cpp, that implicit instantiation is NOT guaranteed to make that instantiated definition available to other translation units. (The compiler may, in effect, generate it with internal linkage.)

See C++17 [temp]/7

A function template, member function of a class template, variable template, or static data member of a
class template shall be defined in every translation unit in which it is implicitly instantiated (17.7.1) unless
the corresponding specialization is explicitly instantiated (17.7.2) in some translation unit; no diagnostic is
required.

What this is saying is that if you need to call a function template from translation unit 1, but you only want to define that function template in translation unit 2, then translation unit 2 must explicitly instantiate the function template with the desired arguments. An implicit instantiation doesn't count.

(Note that the explicit instantiation of myclass<int> will also implicitly instantiate the definitions of all of myclass<int>'s member functions.)



Related Topics



Leave a reply



Submit