Raymii.org
Quis custodiet ipsos custodes?Home | About | All pages | Cluster Status | RSS Feed
C++ template definitions in a .cpp file (instead of a header file)
Published: 22-06-2019 | Author: Remy van Elst | Text only version of this article
❗ This post is over five years old. It may no longer be up to date. Opinions may have changed.
Table of Contents
In this snippet I'll show you how to place your C++ template definitions
in a seperate .cpp
file. I'd recommend you to just put template definitions in
your header file, or a .hpp
file, but if you really want to there is a trick to
get them in a seperate .cpp
file. The trick is to explicitly instanciate every
template you're going to use at the end of the .cpp
file. With many different
templates and types this becomes cumbersome, but for certain usecases it could
be useful.
Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below:
I'm developing an open source monitoring app called Leaf Node Monitoring, for windows, linux & android. Go check it out!
Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.
You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $200 credit for 60 days. Spend $25 after your credit expires and I'll get $25!
Template definitions
Small recap on templates. A template is not an actual class or function, but a "pattern" that the compiler uses to generate a family of classes or functions.
In order for the compiler to generate the code, it must see both the template
definition (not just declaration) and the specific types/whatever used to "fill
in" the template. For example, if you're trying to use a Foo<int>
, the compiler
must see both the Foo
template and the fact that you're trying to make a
specific Foo<int>
. See here for more explanation.
Placing templates in your .h
files might result in cluttered header files, it
also could increase code bloat and the compiled binary size. (That however does
depend on how smart your compiler is). For the cluttering people often resort to
.hpp
files. Which brings its own set of problems, for example with your build
system if you're doing something special.
The trick I found here is that you can place your template definitions in a
seperate .cpp
file and explicitly instanciate every form of that template that
is going to be used in that .cpp
file.
If you don't instanciate all forms in your .cpp
file you will get
undefined reference
errors, I'll show you an example later on.
The linker however does spew out the specific form so you can copy/paste it quickly.
Example code
I've written a sample program with a class with one template function, one other class and the
main.cpp
file. This is the directory layout, you can ignore the CMake
files:
$ tree -L 1
.
|-- CMakeLists.txt
|-- TestClass1.cpp
|-- TestClass1.h
|-- TestClass2.cpp
|-- TestClass2.h
|-- cmake-build-debug
`-- main.cpp
1 directory, 6 files
TestClass1.h
This file contains the class with one template function. It does not contain the template defintion, only the declaration. Normally you would define the entire template here but that's the part we don't want to in this example.
#ifndef TESTCLASS1_H
#define TESTCLASS1_H
#include <iostream>
class TestClass
{
private:
bool m_bool1 { false };
public:
TestClass(bool bool1) : m_bool1(bool1) {}
// just the template declaration
template <typename T1, typename T2>
void templateFunction(T1 var1, T2 var2);
};
#endif //TESTCLASS1_H
TestClass1.cpp
This is where the template is defined, and at the bottom, instanciated explicitly for the types we're going to use in the code.
#include <iostream>
#include "TestClass1.h"
//actual template definiton
template <typename T1, typename T2>
void TestClass::templateFunction (T1 var1, T2 var2) {
std::cout << "var1: " << var1 << ", ";
std::cout << "var2: " << var2 << ", ";
std::cout << "m_bool1: " << m_bool1 << "\n";
}
// Here is the explicit instanciation
template void TestClass::templateFunction<int, int>(int, int);
template void TestClass::templateFunction<char const*, char const*>(char const*, char const*);
TestClass2.h
This is just another class where the template is used, as an example.
#ifndef TESTCLASS2_H
#define TESTCLASS2_H
#include "TestClass1.h"
class TestClass2 {
private:
bool m_abc1 {false};
public:
void printTest();
};
#endif //TESTCLASS2_H
TestClass2.cpp
Here is the definition of the above function, where the other template is called
with a const char *
.
#include "TestClass2.h"
void TestClass2::printTest () {
TestClass example(false);
example.templateFunction ("abc", "def");
};
main.cpp
It all comes together in the main.cpp
file, one of both classes. I've used
two different methods of calling the class templated function, either explicitly
telling which types were using or just letting the compiler figure it out.
#include <iostream>
#include "TestClass1.h"
#include "TestClass2.h"
int main () {
TestClass example1(true);
example1.templateFunction<int, int> (1, 2);
example1.templateFunction (3, 4);
TestClass2 lala = TestClass2();
lala.printTest ();
return 0;
}
Example output:
var1: 1, var2: 2, m_bool1: 1
var1: 3, var2: 4, m_bool1: 1
var1: abc, var2: def, m_bool1: 0
Error, undefined reference
The warning when you forget to instanciate a template, or in this example, uncommented one:
//template void TestClass::templateFunction<int, int>(int, int);
template void TestClass::templateFunction<char const*, char const*>(char const*, char const*);
Output:
[100%] Linking CXX executable example
CMakeFiles/folder.dir/main.cpp.o: In function `main':
folder/main.cpp:7: undefined reference to `void TestClass::templateFunction<int, int>(int, int)'
folder/main.cpp:8: undefined reference to `void TestClass::templateFunction<int, int>(int, int)'
collect2: error: ld returned 1 exit status
If you would use the template with two doubles
you would have to add this at
the end of the file TestClass1.cpp
:
template void TestClass::templateFunction<double, double>(double, double);
In the header file
If the template function for TestClass1
was in the header file, it would look
like this:
TestClass1.h
:
#ifndef TESTCLASS1_H
#define TESTCLASS1_H
#include <iostream>
class TestClass {
private:
bool m_bool1 { false };
public:
TestClass(bool bool1) : m_bool1(bool1) {}
// template declaration and definiton
template <typename T1, typename T2>
void templateFunction (T1 var1, T2 var2) {
std::cout << "var1: " << var1 << ", ";
std::cout << "var2: " << var2 << ", ";
std::cout << "m_bool1: " << m_bool1 << "\n";
}
};
#endif //TESTCLASS1_H
You would not need the TestClass1.cpp
file.