In case you have a project where you use a templated class that is split in its own header (.h
) and source (.cpp
) files, if you compile the class, into an object file (.o
), separately from the code that uses it, you will get the undefined reference to
error at linking.
Lets assume we have Stack.cpp
and Stack.h
which define a templated stack using vectors. And main.cpp
that uses this class after including Stack.h
.
If you try to compile these files as mentioned above, one by one, later you will get a linking error saying undefined reference to
for the methods of the class.
The code in the template is not sufficient to instruct the compiler to produce the methods that are needed by main.cpp
(e.g. Stack<int>::push(...)
and Stack<string>::push(...)
) as the compiler does not know, while compiling Stack.cpp
by itself, the data types it should provide support for.
The reason it allows you to compile these incomplete objects is the following:
main.cpp
: the compiler will implicitly instantiate the template classes Stack<int>
and Stack<string>
because those particular instantiations are requested in main.cpp
. Since the implementations of those member functions are not in main.cpp
, nor in any header file included in main.cpp
(particularly Stack.h
), the compiler will not include complete versions of those functions in main.o
and it will expect to find them in another object during linking.
Stack.cpp
: the compiler won’t compile the instantiations of Stack<int>
and Stack<string>
neither as there are no implicit or explicit instantiations of them in Stack.cpp
nor Stack.h
.
So in the end, neither of the .o
files contain the actual implementations of Stack<int>
and Stack<string>
and the linking fails.
Solutions
Solution 1 : Explicitly instantiate the template
At the end of Stack.cpp
, you can explicitly instantiate all needed templates.
In our example we would add:
template class Stack<int>;
template class Stack<std::string>;
This will ensure that, when the compiler is compiling Stack.cpp
that it will explicitly compile all the code needed for the Stack<int>
and Stack<std::string>
classes.
Using this method, you should ensure that all the of the implementation is placed into one .cpp
file and that the explicit instantation is placed after the definition of all the functions (for example, at the end of the file).
A problem with this method is that it forces you to update the Stack.cpp
file each time you want to add support for a new data type (or remove one).
Solution 2 : Move the implementation code into the header file
Move all the source code of Stack.cpp
to Stack.h
, and then delete Stack.cpp
. Using this method you do not need to manually instantiate all possible data types that are needed and thus you do not need to modify code of the class. As a side-effect, if you use the header file in many other source files, it will compile the functions of the header file in each source. This can make compilation slower but it will not create any compilation/linking problems, as the linker will ignore the duplicate implementations.
Solution 3 : Move the implementation code into a new header file and include it in the original header file
Rename Stack.cpp
to Stack_impl.h
, and then include Stack_impl.h
from Stack.h
to keep the implementation in a separate file from the declaration. This method will behave exactly like Solution 2
.