Introduction: The familiar pain of every C++ developer
\n\nHave you ever written a neat template class, separated the declaration in a .h file and the implementation in a .cpp, compiled the project, and gotten something like "undefined reference to ..." from the linker? If so — you are not alone. This question on Stack Overflow has gathered thousands of views and hundreds of comments. The topic "why can templates only be implemented in the header file" is a real stumbling block for beginners and even experienced programmers transitioning to C++ from other languages.
In this article, we will finally understand why C++ templates require this approach, how the compilation model works, and what to do if you really want to hide the implementation in a .cpp. Let's go!
1. How does the compiler see a template? The C++ compilation model
\n\nTo understand the problem, we need to recall how C++ compiles regular code. The process consists of two stages:
\n\n- \n
- Compilation — each
.cppfile (translation unit) is compiled independently. The compiler only sees what is included in that file via#include. \n - Linking — object files (
.oor.obj) are assembled together, looking for definitions of functions, variables, and classes. \n
Now imagine you have a function template max:
// max.h\ntemplate <typename T>\nT max(T a, T b); // Declaration only!\n\n// max.cpp\n#include "max.h"\ntemplate <typename T>\nT max(T a, T b) {\n return (a > b) ? a : b;\n}\n\n// main.cpp\n#include "max.h"\nint main() {\n int result = max(3, 5); // The compiler sees only the declaration!\n return 0;\n}\n\nWhen compiling main.cpp, the compiler encounters the call max(3,5). It knows that max is a template, but it does not see its implementation. The compiler cannot instantiate the template with type int because the definition is in another file (max.cpp), which is compiled separately. As a result, it generates a call with an external reference, and the linker does not find a suitable instantiated function — hence the error.
This is the root of the problem: C++ templates must be instantiated by the compiler at compile time, and for this, the compiler needs to see the full definition in every translation unit where the template is used.
\n\n2. Why doesn't this work the same way as with regular functions?
\n\nUnlike regular functions, a template is not ready-made code. It is an "instruction" for the compiler: "when you see usage with type int, generate a function for int". If the implementation is hidden in a .cpp, the compiler in main.cpp does not receive this instruction and cannot create the necessary code.
Imagine you have a recipe (template), but the recipe itself is locked in a safe (in another .cpp file). The chef (compiler) only sees the name of the dish but does not know how to cook it. The dish won't be made. For a regular function, the recipe is already ready — the chef simply takes the prepared dish from the refrigerator (from the object file).
3. The classic solution: everything in the header file
\n\nThe simplest and most common way is to place the implementation directly in the header file. This is a standard technique known as "header-only" libraries. For example:
\n\n// max.h\n#ifndef MAX_H\n#define MAX_H\n\ntemplate <typename T>\nT max(T a, T b) {\n return (a > b) ? a : b;\n}\n\n#endif\n\nNow any file that includes max.h gets the full template definition. The compiler can instantiate it for any needed type. This is exactly how popular libraries like Boost and most of the STL work. Disadvant