C++ Class Template Instantiation
My latest project uses templates. However, the implementations of the class templates and the places where they are called are in different files. In normal circumstances, I would organize the classes as a header only library. However, due to problems with CUDA (the class templates use CUDA, while both CUDA and none-CUDA files include them, and due to the fact that all CUDA files need to be linked together first before linking with each other, I do not wish to change the CUDA status of other files), I must put these classes inside a separate .cpp
file and link them later. I came across a few problems during the process, and I’ll document them here.
So we have a class template:
// some_class.h
template<typename T>
class ClassName {
public :
ClassName(T);
T someMethod(T);
};
// some_class.cpp
template<typename T>
T ClassName::someMethod(T t) {
// do something
}
And we use it in another file:
#include "some_class.h"
ClassName<float> a(1.0);
a.someMethod(2.0);
It would compile, but during linking, the linker would say that the class ClassName cannot be found! This is because the compiler would only be able to generator the object code of a class template’s methods when the specific type used on the template is given! Or as the standards says:
A class template by itself is not a type, or an object, or any other entity. No code is generated from a source file that contains only template definitions.
When using header only libraries, this problem does not exist:
When code refers to a template in context that requires a completely defined type, or when the completeness of the type affects the code, and this particular type has not been explicitly instantiated, implicit instantiation occurs.
So when a class template is called with a specific type in mind, the compiler would automatically instantiate an implementation of the class. Similar things happen when its methods are called. This is “implicit instantiation” of a class template with a certain type. However, when the definition and usage of a class template is separated to different translation units, the compiler could not do an implicit instantiation. When compiling the some_class.cpp
file, no instance of the class is created, and no method is called, meaning that the compiler would actually do nothing! So we must explicitly instantiate the class template with the types we would like to use.
My mentor suggested that I simply instantiate instances of the class in some_class.cpp
:
struct ins {
ins(float i, double j, c k) : a(i), b(j), c(k) {}
ClassName<float> a;
ClassName<double> b;
ClassName<int> c;
};
ins(1.0,2.0,1);
However, this would only instantiate the class and not its methods. Further more, this looks more like a “hack”, not to mention the potential side effects. Fortunately, the C++ languages provides a way to explictly instantiate a class template:
template class|struct template-name < argument-list > ;
For example. to instantiate ClassName
for float
, double
and int
, all I need is to add these lines after the definitions:
template class ClassName<float>;
template class ClassName<double>;
template class ClassName<int>;
Voila! Problem solved. Now everything links correctly.
Further Readings: