Template Definitions and Declarations
A template cannot be compiled once in its own file and then linked in later. Every translation unit that uses it needs the definition itself.
A Template That Nobody Can Call
Templates let you write code once and have the compiler produce concrete versions of it for the types you actually use. That flexibility comes with a rule that surprises many programmers: having a template in your program is not enough for the compiler to use it. The compiler needs to see the template's definition at the point where you use it. A declaration that merely announces that a template exists does not give the compiler the body it needs.
What do you think happens?
One file declares a template and a second file tries to call it. What has to be visible to that second file for the call to work?
Reveal answer
The template's definition, meaning its body, must be visible to the second file at the point of the call.
Scope is the reason definitions and declarations behave differently here. A declaration can be visible while the template is still unusable, because the compiler also needs the definition among the things it can see at the place of use.
Following One Template Through Two Files
Read the following example as a trace. The template definition lives in one header, max_util.h, and two separate source files use it. Notice that each source file includes the header itself. Neither file relies on the other for the template body.
max_util.hholds the full template definition, including its bodymain.cppincludes the header, so its translation unit has the definition and can call larger with int argumentsreport.cppincludes the same header, so its own translation unit also has the definition and can call larger
Tracing what each file can see
When the compiler reaches the call to larger in report.cpp, can it resolve that call, and why?
- 1Start with the translation unit for report.cpp
A translation unit is one source file together with everything it includes. The compiler works on
report.cpptogether withmax_util.h. - 2Check what the include delivers
The #include line copies the template definition into this translation unit. The body of larger is therefore visible in
report.cpp. - 3Resolve the call
The call
larger(x, y)appears where the definition is in scope, so the compiler can use the template for the types it receives. - 4Repeat for main.cpp
main.cppalso includesmax_util.h. Its own translation unit receives its own copy of the definition, independent ofreport.cpp.
Both calls work, because each translation unit that uses the template receives the definition through its own include.
The diagram shows the practical consequence. The definition lives in one header, but it reaches every translation unit that uses it through an include. If a translation unit does not include the definition, that unit cannot use the template, even if the rest of the program has it.
Why Templates Cannot Be Compiled Alone
Ordinary C++ code is often split across several source files, and each source file is compiled on its own. A template does not follow this pattern. There is no separate compilation of templates, so a template cannot be compiled once in its own file and then linked in later. Instead, the template definition must be included in every translation unit that uses it.
A translation unit is one source file together with everything it includes. It is the unit the compiler works on. Because a template's definition is not compiled in isolation, each translation unit that needs the template must receive the definition itself through an include.
A template cannot be compiled once in its own file and then linked in later. Every translation unit that uses it needs the definition itself.
When Only the Declaration Is Visible
Now consider a header that contains only a template declaration, with no body. Files that include that header can see that the template exists, but they cannot see what it does. The compiler therefore has no definition from which to produce a concrete version for the types used at the call site.
- The header announces that larger exists but supplies no body
- The call in
report.cppsits where the definition is not in scope, so the template cannot be used in this file
// Only the declaration is in scope
template <typename T>
T larger(T a, T b);
int report(int x, int y) {
return larger(x, y);
}Declaration visible, definition not visible: the call cannot be used
// The definition is in scope
template <typename T>
T larger(T a, T b) {
return (a > b) ? a : b;
}
int report(int x, int y) {
return larger(x, y);
}Definition visible at the call: the compiler can use the template
Mistakes Beginners Make with Template Headers
Common mistake Putting only the template declaration in the header
What it looks likeA header contains template T larger(T a, T b); and no body, and a .cpp file calls larger(x, y) after including it.
Why it failsA declaration announces that a template exists but does not give the compiler the body it needs at the place of use.
Do this insteadPlace the full template definition in the header so every file that includes it can use the template.
Common mistake Assuming the definition in one file helps another file
What it looks likemain.cpp includes the full definition, and report.cpp includes only the declaration, but report.cpp still calls larger.
Why it failsTemplates have no separate compilation. Each translation unit only has what it includes, so
report.cppcannot use a definition it never received.Do this insteadMake report.cpp include the header that contains the full definition.
Common mistake Compiling the template in its own .cpp file and expecting it to be linked later
Why it failsA template cannot be compiled once in its own file and then linked in later, because its definition must be included in every translation unit that uses it.
Do this insteadKeep the definition in the header and include that header wherever the template is used.
Common mistake Forgetting that a translation unit includes everything its source file includes, so a missing include silently removes the definition from that unit
Checking Your Understanding of Template Headers
A team puts the declaration of a template function in util.h and the full definition in util.cpp. Two other files include util.h and call the template. Explain why those calls may not work, and describe how you would restructure the code.
Hints
- Ask what each translation unit can see when the compiler reaches the call
- Recall that templates have no separate compilation
- Think about where the definition must live so that each including file receives it
Key Takeaways
- A template is usable only when its definition, not just its declaration, is in scope at the point of use.
- Templates have no separate compilation, so their definitions must be included in every translation unit that uses them.
- Each translation unit receives the template definition through an include, so a file that includes only a declaration cannot use the template.
- Putting only a template declaration in a header is a common mistake, and the fix is to place the full definition in the header that other files include.