Concepts / Best Practices for Template Programming

Best Practices for Template Programming

Read a template as a fixed recipe with blanks. The recipe is the algorithm. The blanks are the type slots, and each use of the template fills the blanks with a concrete type.

  • Programming

Why One Logic Serves Many Types

Many programs need the same logic over and over again, but applied to different kinds of data. A sorting routine, a stack of items, or a list of values all follow a pattern that does not really depend on whether the values are integers, decimal numbers, or text. Templates are the tool for describing code this way: the pattern is written once, and the specific type of data is supplied later, when the code is used.

This article focuses on three uses that the source highlights. The first is expressing an algorithm that applies to many argument types. The second is expressing a container, so that one definition can hold elements of different types. The third is the effect both of these have on the level of abstraction of code. Each one is traced below, step by step, so you can see what state the code is in at each point.

Predicting the Cost of Duplication

Before reading on, think about a routine that finds the largest of several values. Suppose you need it for integers, for decimal numbers, and for text. Consider how much of the code you would have to write if you did not use templates.

What do you think happens?

If the same algorithm must work for integers, decimal numbers, and text, how many separate definitions does a template approach require?

Reveal answer
Answer:

One template definition. The element type is left open and is supplied by each argument type that uses it.

Without templates, the shared structure of the algorithm would be repeated for each type. A template keeps that structure in one place and leaves the type slot open.

Templates as Patterns With Open Slots

Definition

A template is a pattern with open type slots. The written logic refers to general roles, and concrete types fill those slots later. Templates are used to express algorithms that apply to many argument types.

The key idea is separation. The steps of the algorithm are written in terms of the operation being performed, while the specific type of the data is left open. When the algorithm is needed for a particular argument type, that type is supplied, and the same written logic serves it. The logic does not need to be rewritten for each type; only the type changes.

Key point

Read a template as a fixed recipe with blanks. The recipe is the algorithm. The blanks are the type slots, and each use of the template fills the blanks with a concrete type.

Seeing One Template Become Several Versions

When one template is used with several argument types, each use produces its own typed version of the pattern. The diagram below shows a single template with an open type slot T. Supplying int, double, and string gives three typed versions, and all three trace back to the same written definition.

Template patternopen type slot TVersion for intT filled with intVersion for doubleT filled with doubleVersion for stringT filled with stringsupply intsupply doublesupply string
When the same template is used with int, double, and string, what typed versions result, and how do they all trace back to one definition?

The three typed versions behave differently in the sense that each one works with its own element type, but they share the same structure. A change to the template's logic is a change to all of them at once, because they all come from one definition. This is the practical benefit of expressing the algorithm once.

Tracing a Largest-Value Routine

Worked example

Finding the Largest Value With One Template

A routine finds the largest value in a sequence. Its logic is written once as a template over an element type T. Trace how the same written steps are applied first to integers and then to text.

  1. 1
    Write the general steps

    Start with the first element as the current largest. For each remaining element, compare it with the current largest and keep whichever is greater. The steps refer to an element of type T, not to any specific type.

  2. 2
    Supply the first argument type

    Use the template with int. The type slot T is now filled with int, and the comparison works on integer values. The written steps are unchanged.

  3. 3
    Run on an integer sequence

    Given the values 3, 9, 4, the current largest starts at 3. Comparing with 9 keeps 9, and comparing with 4 keeps 9. The result is 9.

  4. 4
    Supply a second argument type

    Use the same template with string. The slot T is now filled with string, and the comparison works on text values. The same steps are reused without being rewritten.

  5. 5
    Run on a text sequence

    Given the values pear, apple, plum, the current largest starts at pear. Comparing with apple keeps pear, and comparing with plum keeps plum. The result is plum, the greatest in the order the comparison defines.

Result

One written algorithm produced correct results for two different argument types. Only the type supplied at each use changed, not the steps.

Which Parts Stay Fixed

The diagram below separates what a template fixes from what each argument type supplies. The algorithm steps are written once. The operation the steps perform is supplied through use, and the element type is supplied by the argument. Together these produce a result that works for many types.

Algorithm stepswritten once, fixedOperationapplied through useApplied resultworks for many typesElement typesupplied by argumentcallsapplies toyieldsyields
How do the steps of one algorithm stay the same while the element type varies, and which parts are fixed versus supplied by each argument type?

Expressing a Container Once

Without templates, a container for integers and a container for text would each need its own separate definition, even though their structure is the same. Expressing the container as a template keeps the shared structure in one place and leaves the element type to be chosen by whoever uses it. The source gives this as one of its core uses of templates.

AspectSeparate definitionsOne template container
Shared structureRepeated in each definitionKept in one place
Element typeFixed inside each definitionChosen by the user at each use
Container for integersIts own definitionThe template with the integer type supplied
Container for textIts own definitionThe same template with the text type supplied
Change to shared structureEdit every definitionEdit the one template

Comparing separate type-specific containers with a single template container

Raising the Level of Abstraction

Abstraction means working with the idea of something rather than its every concrete detail. Templates raise the level of abstraction of code: the code is written in terms of general roles such as element, operation, or container, and concrete types are attached later. A reader can therefore understand what an algorithm or container does without tracing every specific type it might be used with.

In the real world

Consider the largest-value routine from the earlier trace. A reader who sees that it compares elements and keeps the greater one already understands its behavior for integers, decimal numbers, and text. They do not need a separate explanation for each type, because the general roles carry the meaning and the types are attached only at use.

Best practice

When writing a template, name the general roles it depends on, such as element, operation, or container, and write the logic in those terms. Attach concrete types only where a particular use requires them. This keeps the code at a level where its purpose is visible.

Mistakes When Writing Templates

  • Common mistake

    Copying the algorithm once for each type

    What it looks like

    Writing one largest-value routine for integers, another for decimal numbers, and another for text, with identical steps in each.

    Why it fails

    The shared logic is repeated, so any later change must be made in several places. The purpose of a template is to hold that shared structure in one place.

    Do this instead

    Write the steps once in terms of a general element type, then supply the type at each use.

  • Common mistake

    Fixing the element type inside the template

    What it looks like

    Writing the template's comparison logic as if every element were an integer.

    Why it fails

    The template is then no longer applicable to many argument types. Its type slot has been closed before any argument was supplied.

    Do this instead

    Refer to the element through the general role it plays, and leave the concrete type open for each use.

  • Common mistake

    Treating a template as a single concrete routine

    What it looks like

    Looking at the template definition and expecting one fixed function that works for every type at once.

    Why it fails

    A template is a pattern with open slots. Each use fills the slots with a concrete type, producing a typed version of the pattern.

    Do this instead

    Think of the template as the pattern, and each use as one typed version that the pattern produces.

  • Common mistake

    Confusing abstraction with vagueness

The last item above needs a note. Abstraction does not mean leaving the logic unclear. It means describing the logic in general roles so that the concrete detail can be attached cleanly later. A clearly written template is abstract in this sense; a vague one is simply hard to follow.

Practice Separating Fixed From Supplied

MEDIUM

A sorting routine is written once as a template. For this routine, list which parts are fixed in the template and which parts are supplied by the argument type at each use. Then explain what changes when the routine is used first with integers and later with text.

Hints
  • Start with the steps of the sorting routine. Are they written in terms of a particular type, or in terms of general roles?
  • Ask which part changes between the two uses: the steps, or the type filled into the slot.

Key Takeaways

  1. 1
    A template expresses an algorithm once as a pattern with open type slots, so the same written logic can apply to many argument types.
  2. 2
    Each use of a template fills its type slots with concrete types, producing a typed version of the pattern.
  3. 3
    A template can also express a container, keeping the shared structure in one place and leaving the element type to the user.
  4. 4
    Templates raise the level of abstraction of code, so the general roles of an algorithm or container can be understood apart from each concrete type.

At a glance

  • A template expresses an algorithm once as a pattern with open type slots, so the same logic applies to many argument types.
  • Each use of a template supplies concrete types, producing a typed version of the same pattern.
  • Expressing a container as a template keeps its shared structure in one place and leaves the element type to be chosen by the user.
  • Templates raise the level of abstraction of code by describing it in general roles that are understood apart from each concrete type.

Related concepts

More concepts starting with B