Concepts / Translation Units and Include Files

Translation Units and Include Files

Each translation unit that uses a template must receive the definition through an include. A definition that exists only in a different file does not make the template usable in the file that calls it.

  • Programming

Why Having a Template Is Not Enough

A template is a way of writing code once and letting 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 somewhere 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 to build a version of that template.

Keep this question in mind as you read: if one file declares a template and another file tries to call it, what has to be visible to that second file for the call to work? The answer depends on the difference between a declaration and a definition, and on the way compilation handles templates. Once you see that difference, a whole category of confusing compiler errors becomes predictable.

Declarations Versus Definitions in Scope

Scope is the reason definitions and declarations behave differently here. Both can be visible at the same time, yet the template still cannot be used unless the definition is among the things the compiler can see at the place of use. A declaration tells the compiler the template's name and its shape, but it does not carry the body. A definition carries the body, and that body is what the compiler needs in order to produce a concrete version for a particular type.

Templatedeclarationname and signatureBody not visibleno code to instantiateTemplatedeclarationname and signatureTemplate bodycode the compilerinstantiatesCall can be usedconcrete version for thetypedoes not provideprovidesenables
What the compiler can see at a call site when the header holds only the template declaration, compared with when it holds the full definition. Only the second case lets the call be used.

Why Templates Have No Separate Compilation

Ordinary C++ code is often split across several source files, and each source file is compiled on its own. You can compile one .cpp file into an object file, compile another .cpp file separately, and then link the results together. Templates do 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 for every caller.

Because of this, the template definition must be included in every translation unit that uses it. A translation unit that calls the template needs to have the definition itself, delivered through an include. The definition is not handed over as a finished, precompiled piece of code that other files can pick up at link time. Each file that uses the template has to receive the definition and work with it directly.

Key point

Each translation unit that uses a template must receive the definition through an include. A definition that exists only in a different file does not make the template usable in the file that calls it.

The Translation Unit as the Unit of Work

A translation unit is one source file together with everything it includes. This is the unit the compiler works on. When you compile a .cpp file, the compiler does not see just that file; it sees the file with all of its included headers pulled in as part of its text. That combined whole is the translation unit, and it is what decides what names and definitions are available at each point in the code.

Because a template's definition is not compiled in isolation, each translation unit that needs the template must receive the definition itself. The include directive is the mechanism that does this. When a header is included, its contents become part of the file the compiler is reading, and any template defined there is visible to every line of code that follows the include.

largest.hholds the templatedefinitiona.cpp unitsource file plus itsincludesConcrete version ina.cppbuilt for the types a.cppusesb.cpp unitsource file plus itsincludesConcrete version inb.cppbuilt for the types b.cppuses#include#includeinstantiatedinstantiated
A single header holds the template definition. Each source file that includes it forms its own translation unit, and each unit can produce its own concrete version of the template for the types it uses.

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. The header is therefore the single place where the definition is written, and the include is the path by which it arrives everywhere it is needed.

Tracing a Template Through Two Source Files

The following example is an original illustration, not taken from the source. It places a template definition in a header and uses it from two separate source files. Read it as a trace: follow what each file can see when the compiler reaches a call to the template.

cpp
cpp
cpp
Worked example

Tracing the calls in each translation unit

a.cpp calls largest(3, 7) and b.cpp calls largest(10, 4). Both include largest.h. What does each translation unit see when the compiler reaches its call?

  1. 1
    Start with a.cpp

    The preprocessor replaces the include line with the contents of largest.h, so the template definition becomes part of a.cpp's text before the call on the main line.

  2. 2
    Check the call in a.cpp

    When the compiler reaches largest(3, 7), the definition is already in scope. It can build the version that works with int values.

  3. 3
    Start with b.cpp

    b.cpp includes the same header, so it receives its own copy of the definition text as part of its own translation unit.

  4. 4
    Check the call in b.cpp

    When the compiler reaches largest(10, 4), the definition is in scope in this unit too. The unit does not depend on a.cpp for the template body.

Result

Each translation unit receives the definition through its own include and builds what it needs. Neither file relies on the other for the template body, which is exactly what the absence of separate compilation for templates requires.

What Goes Wrong When the Definition Is Missing

Now suppose the header keeps only the declaration, and the definition is left out. The file below is an original illustration of that mistake, built on the same two source files as before.

cpp

With this header, b.cpp still includes largest.h and still calls largest(10, 4). The declaration makes the name known, but the compiler has no body to turn into a version for int. The source states that a header containing only a template declaration cannot support template calls in files that include it. The error that results depends on the toolchain: it may be reported while compiling the calling file, or it may appear as an undefined reference when the program is linked.

b.cppcalls largest withdeclaration onlyCompilerhandles the translationunitLinkerjoins object filescall with no body in viewobject file passed onundefined reference reported
A translation unit calls the template while only the declaration is visible. The compiler handles that unit, and the problem surfaces as an error. The exact stage that reports it can vary by toolchain, so the final message may be a compile error or an undefined reference at link time.

Common Mistakes With Template Headers

  • Common mistake

    Putting only the template declaration in the header

    What it looks like

    largest.h contains template <typename T> T largest(T a, T b); and nothing else.

    Why it fails

    A declaration announces the template but does not provide the body the compiler needs to build a version for a type.

    Do this instead

    Place the full definition in the header, so every file that includes it can see the body at the point of use.

  • Common mistake

    Assuming one file's definition is enough for another file

    What it looks like

    a.cpp includes largest.h with the full definition, and b.cpp calls largest without including anything that defines it.

    Why it fails

    Templates have no separate compilation, so the definition must be included into every translation unit that uses it.

    Do this instead

    Include the header that holds the definition in each source file that calls the template.

  • Common mistake

    Believing that a declaration in a header makes the template usable everywhere

    Why it fails

    Both declaration and definition can be visible at once, yet the template still cannot be used unless the definition is in scope at the place of use.

    Do this instead

    Check what is visible at the call site, not just whether the name is declared somewhere in the program.

  • 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 linked in later for other callers.

Checking Your Understanding

EASY

A header named tools.h contains only the declaration template <typename T> T clamp(T v, T lo, T hi); and no body. The file main.cpp includes tools.h and calls clamp(5, 0, 3). A second file, util.cpp, includes tools.h and contains no call to clamp. Which translation unit cannot use the template, and what change would make main.cpp able to use it?

Hints
  • Ask what the compiler must see at the place where clamp is called, not whether the name is declared.
  • A template body must be available in each translation unit that uses it, and it cannot be compiled once and linked in later.
  • Consider what would have to go into tools.h for main.cpp to get the body through its include.

Key Takeaways

  • A template is usable only when its definition, not just its declaration, is in scope at the place where it is used.
  • Templates have no separate compilation, so the definition must be included in every translation unit that uses the template.
  • A translation unit is a source file together with everything it includes, and the include directive delivers the template definition to each unit that needs it.
  • A header with only a template declaration cannot support calls to the template in files that include it, and the resulting error may appear at compile time or as an undefined reference at link time depending on the toolchain.

Related concepts

More concepts starting with T