Skip to content
VLSI Mentor

C++ Part I · Module 18 · Templates and Generic Programming

Instantiation: When a Template Becomes Code

This module's brief puts the emphasis on one idea, and four chapters have now invoked it without examining it: a template is not code until a type is supplied. That sentence is the right place to start and it is not quite accurate, which this chapter establishes by putting two separate pieces of nonsense inside a function template that nothing in the program ever calls. One of them compiles and one of them does not, and the line separating the two cases is precise enough to predict every other template behaviour in this module. The approximate version explains a great deal on its own. It explains why a member function with outright garbage in it passed in the previous chapter but one, why the diagnostic for a bad type argument is reported inside somebody else's header and blamed on your call site, and why the code size measured in the first chapter grows once per type. The exact version explains more. It explains why a helper function has to be declared above the template that calls it rather than merely somewhere in the file, which is a mistake this chapter's author made for real while writing a later chapter in this very module and had to be told about by the compiler. And it explains the one construct that lets you ask for a template to be compiled for a type you are not currently using, which is both a build technique and the only practical way to get a class template's untested members checked.

Advanced19 min readC++Part IFundamentalsTemplates

Part I · Module 18 · Chapter 18.6 · Instantiation: When a Template Becomes Code

1. The Approximate Rule

Chapter 18.1 §7 stated it:

A template is not code. It is instructions for generating code, and nothing is generated until a type is supplied.

Instantiation is that generation. smaller(3, 7) causes the compiler to produce smaller<int> — a real function with real instructions, which Chapter 18.1 §5 measured as five instructions byte-identical to a hand-written version.

Three things this already explains. Chapter 18.1 §6's code size grows per type because each instantiation is separate code. Chapter 18.4 §3's instantiations are distinct types because each is separately generated. And Chapter 18.4 §7's uncalled member compiled because it was never generated at all.

§2 shows where the approximation breaks.

2. Where the Approximate Rule Fails

A function template nobody calls, containing two separate pieces of nonsense:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    template <typename T>
    void neverCalled(T t)
    {
        t.thisMemberDoesNotExist();          // nonsense -- but is it compiled?
        int x = "not an int";                 // also nonsense
        (void)x;
    }
    int main() { /* neverCalled is never called */ }
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
compile exit: 1   errors: 1
lazy.cpp:6:9: error: cannot initialize a variable of type 'int' with an lvalue of type 'const char[11]'

One error, not two, and it is the second line. The template was never instantiated, and yet int x = "not an int"; was rejected while t.thisMemberDoesNotExist() was not.

Isolated, to be certain:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
DEPENDENT      (t.thisMemberDoesNotExist()) : COMPILES (deferred to instantiation)
NONDEPENDENT   (int x = "not an int";)      : error: cannot initialize a variable of type 'int' …

So the one-line rule is a useful approximation and the precise rule is §3's.

3. The Precise Rule: Two Phases

Phase 1, at the point of definition: everything that does not depend on a template parameter is checked. Phase 2, at each instantiation: everything that does depend on a template parameter is checked.

That is two-phase lookup, and it accounts for §2 exactly. int x = "not an int"; mentions no template parameter, so it is ordinary code in an ordinary function and is checked immediately. t.thisMemberDoesNotExist() depends on T — whether it is valid is unanswerable until T is known — so it waits.

ConstructDepends on T?Checked
int x = "not an int";noat definition
t.member()yesat instantiation
t + tyesat instantiation
std::vector<int> v;noat definition
std::vector<T> v;yesat instantiation
a syntax error—at definition — parsing needs no types

Syntax is always checked immediately. A missing brace or a malformed declaration in an uninstantiated template is an error, because the compiler must parse the template to store it at all.

4. What Instantiation Produces

For a function template, a function. For a class template:

Instantiating the class produces the declarations — enough for its size, layout and interface. Chapter 18.4 §5 measured sizeof(Box<char>) as 1, which requires the layout to exist.

Instantiating a member produces that member's body, and only when it is used. Chapter 18.4 §7 measured it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
compile exit: 0   errors: 0
brokenUnlessCalled() was never called -- and never compiled

That asymmetry is the practically important one. A class template's declarations are all checked; its member bodies are checked one at a time, on demand.

5. Why the Error Appears at the Call Site

Now the second half of this chapter's brief. Instantiate the broken template:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    template <typename T> void neverCalled(T t) { t.thisMemberDoesNotExist(); }
    int main() { neverCalled(42); }
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
error: member reference base type 'int' is not a structure or union
note: in instantiation of function template specialization 'neverCalled<int>' requested here

The error is in the template; the note is at the call. That pairing is forced by §3: the construct could not be checked until T was known, T became known because of this call, so the failure belongs to the template's body and the reason belongs to the call site.

note: in instantiation of … requested here is the line that tells you which of your calls was wrong. The error: line tells you what the template could not do.

Chapter 18.2 §8 measured this for a missing operator< and Chapter 18.7 shows how to use it on a diagnostic hundreds of lines long. The reading order is: find the last in instantiation of note mentioning your file, because that is your code.

6. Instantiation Is Per Type, Once

The compiler instantiates smaller<int> once per translation unit however many times you call it, and the linker merges duplicates across translation units — which is why Chapter 18.7 §5's two-TU case links cleanly rather than reporting a duplicate symbol.

Two consequences.

Instantiation is not free at compile time. Each distinct instantiation is a separate piece of code to generate and optimise, which is a large part of why template-heavy code compiles slowly.

And it is deduplicated at run time. Chapter 18.1 §6's 843 bytes for eight types is eight functions, not eight per call site.

7. A Dependent Name Still Needs to Be Findable

The sharper rule has a consequence that catches people, and it caught me.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    template <typename T> void show(const T& v) { helper(v); }   // helper not yet declared
    static void helper(int v) { /* ... */ }
    int main() { show(42); }
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
error: call to function 'helper' that is neither visible in the template definition nor found by
       argument-dependent lookup
note: in instantiation of function template specialization 'show<int>' requested here
note: 'helper' should be declared prior to the call site

Declaring helper later in the same file is not enough. For a name that is not dependent on T, phase 1 does the lookup — at the point of definition — so helper must already be visible there. Moving it above the template fixes it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  helper(42)

The escape hatch is argument-dependent lookup: if helper were found via its argument's namespace, it would be looked up in phase 2 and a later declaration would work. That is why library code often relies on ADL, and it is also why the error message mentions it.

This is not a hypothetical. I hit it writing Chapter 18.9's variadic program: three toStr overloads were defined below the template that called them, and the diagnostic above is verbatim what I got. The fix was to move them up. A rule that produces an error message naming its own remedy is a good rule, and this is one of the few in template-land that does.

8. Forcing Instantiation Deliberately

The construct that follows from all of this:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    template class Box<int>;                 // explicit instantiation of the CLASS
    template int twice<int>(int);            // explicit instantiation of a FUNCTION

Explicit instantiation tells the compiler to generate the code now, for this type, whether or not anything uses it.

Two uses, and the second is the one worth adopting.

A build technique — Chapter 18.7 §3 measures it making a template defined in a .cpp usable from elsewhere, and it can cut compile times by instantiating common types once.

And a test. Chapter 18.4 §7's measurement showed that an uncalled member has never been compiled, so a class template with twenty members used three ways has checked a fraction of itself. Explicit instantiation of the class instantiates all its member bodies:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    template class Stack<int>;               // compiles every member for int
    template class Stack<std::unique_ptr<int>>;

Two lines in a test file turn "these instantiations are supposed to work" into a compile-time check, which is as close as C++17 gets to testing a template's interface without writing a call for every member.

9. What This Chapter Explains

Collecting it, because the module's brief says this idea is the point:

ObservationExplained by
code size grows per type (Chapter 18.1 §6)instantiation generates separate code
instantiations are unrelated types (Chapter 18.4 §3)each is separately generated
an uncalled member with nonsense compiles (Chapter 18.4 §7)member bodies instantiate on demand
the error is in the template, the note at the call (§5)the check could not happen until T was known
a helper must be declared above the template (§7)non-dependent names are looked up in phase 1
a syntax error is caught immediately (§3)parsing precedes types
definitions must be in headers (Chapter 18.7)instantiation needs the definition
the diagnostics are long (Chapter 18.7)one failure, reported through every layer of instantiation

10. What to Actually Do

Hold the one-line rule as your working model — it predicts correctly nearly always.

Reach for the two-phase rule when the approximation surprises you — ask whether the construct mentions a template parameter.

Declare helpers above the templates that call them, unless they will be found by ADL.

Read the last in instantiation of … requested here note to find which of your calls was wrong.

Expect syntax errors immediately — parsing does not wait for types.

Use template class X<T>; in a test file for every instantiation you claim to support.

And expect compile time to grow per instantiation, which is the cost this module has not otherwise priced.

11. Mistakes Worth Making Once

Believing nothing at all is checked before instantiation. Measured: the non-dependent line was rejected in an uninstantiated template.

Believing everything is checked. Measured: the dependent line was not.

Declaring a helper below the template that calls it. Measured, with a diagnostic naming its own fix.

Reading only the first error line of a template diagnostic. The note names your code.

Assuming a class template is fully compiled. Member bodies instantiate on demand.

Shipping a class template without explicit instantiations in a test. Most of it has never been compiled.

Expecting duplicate instantiations across TUs to be a link error. They are merged.

Forgetting that instantiation costs compile time, once per distinct type.

12. Interview Relevance

"When is a template compiled?" The useful answer is at instantiation; the precise answer is two-phase — non-dependent constructs at definition, dependent ones per instantiation. Giving both, and knowing which is the approximation, is the strong answer.

"Can a template nobody uses contain an error?" Yes for a dependent one, no for a non-dependent one. Measured, with two nonsense lines in one uninstantiated function and only one rejected.

"Why is the error reported inside the template and blamed on my call?" Because the check waited for the type, and your call supplied it. The in instantiation of note is the bridge.

"Why must a helper be declared above the template?" Non-dependent names are looked up in phase 1, at the definition. ADL is the exception.

"Is a whole class template compiled when you instantiate it?" No — declarations yes, member bodies on demand.

"How would you make sure a class template's members all compile for a type?" template class X<T>; — explicit instantiation, which compiles every member.

"Why is template-heavy code slow to compile?" One instantiation per distinct type, each generated and optimised separately.

13. What You Should Be Able to Say Now

  1. Instantiation is the generation of real code from a template, triggered by supplying a type.
  2. The one-line rule — nothing is code until a type is supplied — predicts correctly nearly always.
  3. Measured: an uninstantiated template with two nonsense lines produced one error, for the non-dependent one.
  4. Measured, isolated: the dependent construct compiled; the non-dependent one did not.
  5. The precise rule is two-phase: non-dependent constructs at definition, dependent ones at each instantiation.
  6. Syntax is always checked immediately — parsing precedes types.
  7. A class template's declarations are all checked; its member bodies on demand — Chapter 18.4 §7.
  8. Measured: instantiating the broken template gave the error in the template and a note: in instantiation of … requested here at the call.
  9. That note is how you find which of your calls was wrong.
  10. Instantiation happens once per type per translation unit, and duplicates are merged at link time.
  11. So compile time grows per distinct instantiation — the cost this module had not otherwise priced.
  12. Measured: a non-dependent helper declared below the template is an error, with a note saying 'helper' should be declared prior to the call site.
  13. ADL is the exception — a name found through an argument's namespace is looked up in phase 2.
  14. I hit that for real writing Chapter 18.9's program, and had to move three overloads above the template.
  15. template class X<T>; forces instantiation of every member — a build technique and a test.
  16. The question to carry is: does this construct mention a template parameter? Everything else follows.

14. Exercises

Exercise 1 — predict. An uninstantiated template containing t.frobnicate();, double d = "text";, and if (x { . Say which are errors and why.

Exercise 2 — state both rules. Give the one-line rule and the two-phase rule, and say when each is the right one to use.

Exercise 3 — classify. For T x;, int y = 1;, std::vector<T> v;, std::vector<int> w;, t.size(), say which phase checks each.

Exercise 4 — read the diagnostic. Given a 40-line template error, describe the two lines you would look for first.

Exercise 5 — the helper. Write a template calling a helper declared below it, give the diagnostic, and give two fixes.

Exercise 6 — the class template. For a class template with five members used in one way, say how many member bodies have been compiled.

Exercise 7 — force it. Write the explicit instantiations that would compile all five, and say where you would put them.

Exercise 8 — the table. Pick three rows of §9's table and explain each from the two-phase rule alone.

15. Summary

  • Instantiation generates real code from a template when a type is supplied.
  • The one-line rule is a good approximation; the exact rule is two-phase.
  • Measured: in an uninstantiated template, the non-dependent nonsense was rejected and the dependent nonsense was not.
  • Syntax is always checked immediately; a class template's declarations are checked and its member bodies on demand.
  • Measured: the error is reported in the template with a note: in instantiation of … requested here naming your call — which is the line to read first.
  • Instantiation is once per type per TU, merged at link time, so compile time grows per distinct type.
  • Measured: a non-dependent helper must be declared above the template, with ADL as the exception — a mistake I made for real in this module.
  • template class X<T>; compiles every member — both a build technique and the cheapest way to test a class template.
  • The question that explains everything: does this construct mention a template parameter?

Two chapters follow from this one, and the first is the practical consequence that trips up everyone writing their first template in a real project. If a template's definition must be available wherever it is instantiated, and instantiation happens in whichever translation unit uses it, then a template definition hidden in a .cpp file cannot be instantiated from anywhere else — and the failure is not a compile error but a link error, naming a symbol that was never generated. The next chapter measures that, measures the two ways out of it, and then turns to the other thing this chapter's §5 only hinted at: a diagnostic from a failed instantiation deep inside the standard library, which it measures at three hundred and seventy lines, and a method for reading one that works.

Standards & specifications

Governing standard
ISO/IEC 14882 (the C++ language standard)(opens ISO/IEC (JTC 1/SC 22/WG 21) in a new tab)

Defines the C++ language and its standard library — the type system and templates, the object model and object lifetime, initialisation and overload resolution, the memory model and the containers, algorithms and utilities the library provides. Like C's standard it is a FAMILY of editions (C++98/03, C++11, C++14, C++17, C++20, C++23) and the editions are NOT interchangeable: a feature this curriculum relies on may be unavailable, differently specified or merely proposed in another edition, so each lesson names the edition its claim depends on. It does not define any foreign-function interface to a hardware description language, nor what a compiler actually emits, nor platform behaviour on real silicon — those are the implementation's and the device's contracts. Working drafts published by WG21 are freely available and are what most engineers read day to day; a draft is not the published standard.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the C++ Programming curriculum.