This is a text-only version of the following page on https://raymii.org: --- Title : C++ template definitions in a .cpp file (instead of a header file) Author : Remy van Elst Date : 22-06-2019 URL : https://raymii.org/s/snippets/Cpp_template_definitions_in_a_cpp_file_instead_of_header.html Format : Markdown/HTML --- 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. <p class="ad"> <b>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:</b><br><br> <a href="https://leafnode.nl">I'm developing an open source monitoring app called Leaf Node Monitoring, for windows, linux & android. Go check it out!</a><br><br> <a href="https://github.com/sponsors/RaymiiOrg/">Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.</a><br><br> <a href="https://www.digitalocean.com/?refcode=7435ae6b8212">You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $100 credit for 60 days. </a><br><br> </p> ### 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][1]. 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][2] 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. [1]: http://web.archive.org/web/20190621081506/https://isocpp.org/wiki/faq/templates#templates-defn-vs-decl [2]: http://web.archive.org/web/20190621081506/https://isocpp.org/wiki/faq/templates#separate-template-fn-defn-from-decl --- License: All the text on this website is free as in freedom unless stated otherwise. This means you can use it in any way you want, you can copy it, change it the way you like and republish it, as long as you release the (modified) content under the same license to give others the same freedoms you've got and place my name and a link to this site with the article as source. This site uses Google Analytics for statistics and Google Adwords for advertisements. You are tracked and Google knows everything about you. Use an adblocker like ublock-origin if you don't want it. All the code on this website is licensed under the GNU GPL v3 license unless already licensed under a license which does not allows this form of licensing or if another license is stated on that page / in that software: This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Just to be clear, the information on this website is for meant for educational purposes and you use it at your own risk. I do not take responsibility if you screw something up. Use common sense, do not 'rm -rf /' as root for example. If you have any questions then do not hesitate to contact me. See https://raymii.org/s/static/About.html for details.