Concepts / Template Definitions and Declarations

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.

  • Programming

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?

  • Only the template's declaration

  • The template's definition, meaning its body

  • Nothing, because the first file already has the template

Reveal answer
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.

cpp
Notes on the code
  • max_util.h holds the full template definition, including its body
  • main.cpp includes the header, so its translation unit has the definition and can call larger with int arguments
  • report.cpp includes the same header, so its own translation unit also has the definition and can call larger
Worked example

Tracing what each file can see

When the compiler reaches the call to larger in report.cpp, can it resolve that call, and why?

  1. 1
    Start with the translation unit for report.cpp

    A translation unit is one source file together with everything it includes. The compiler works on report.cpp together with max_util.h.

  2. 2
    Check 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.

  3. 3
    Resolve 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.

  4. 4
    Repeat for main.cpp

    main.cpp also includes max_util.h. Its own translation unit receives its own copy of the definition, independent of report.cpp.

Result

Both calls work, because each translation unit that uses the template receives the definition through its own include.

max_util.h(definition)Translation unit:main.cppTranslation unit:report.cppincludeinclude
When one header is included into two .cpp files, the definition lives in the header and each translation unit receives its own copy through the 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.

Definition

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.

Declaration inheaderDefinition in one.cpp fileDeclaration inheaderDefinition in headerIncluded into everytranslation unitdefined oncedefinition providedinclude
An ordinary function's definition is compiled once in one .cpp file, while a template's definition must be included into every translation unit that uses it.
Key point

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.

cpp
Notes on the code
  • The header announces that larger exists but supplies no body
  • The call in report.cpp sits where the definition is not in scope, so the template cannot be used in this file
Before
// 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

After
// 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

Header withdeclaration onlyCompiler onreport.cppCall larger(x, y)1. declaration seen2. call reaches compiler3. no body to use: call cannot be resolved
When the header supplies only a declaration, the compiler sees the template's name but has no body to work from at the call site.

Mistakes Beginners Make with Template Headers

  • Common mistake

    Putting only the template declaration in the header

    What it looks like

    A 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 fails

    A declaration announces that a template exists but does not give the compiler the body it needs at the place of use.

    Do this instead

    Place 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 like

    main.cpp includes the full definition, and report.cpp includes only the declaration, but report.cpp still calls larger.

    Why it fails

    Templates have no separate compilation. Each translation unit only has what it includes, so report.cpp cannot use a definition it never received.

    Do this instead

    Make 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 fails

    A 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 instead

    Keep 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

MEDIUM

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.

Related concepts

More concepts starting with T