Concepts / Template Type Checking

Template Type Checking

In C++ templates, suitability is judged by use. Nothing in the template has to name a type or an interface in advance for this to work.

  • Programming

Asking Whether an Argument Is Suitable

Generic code is code written once that works for many kinds of data. A function that doubles a number, or a function that finds the largest of two items, does not care exactly which type it receives as long as the operations it needs are available. This raises a practical question for the language: how does it decide whether a particular argument is suitable for the generic code?

Many programmers expect a generic piece of code to announce what it needs. An interface, or a list of required operations, would let a reader see the demands up front and would let the language reject a bad argument with a clear message. C++ templates take a different route. A template declaration does not list the operations an argument must support. Instead, the checking happens when the template's definition uses the argument. This article follows that process step by step, so you can see exactly where the suitability of an argument is decided.

Duck Typing Applied to Templates

The idea behind this arrangement is called duck typing. Its informal statement is that if something walks like a duck and quacks like a duck, then it is a duck. Applied to templates, the question is not what category an argument belongs to, but whether it behaves the way the template needs. The template is satisfied when the argument supports the operations the definition actually performs on it.

Think of duck typing as asking a behavioral question instead of a labeling question. A label would say this argument is an integer or this argument is a string. A behavioral question says this argument can be added to itself, or this argument has a size. The template never needs the argument to be a member of a named family. It only needs the specific behaviors that its body relies on.

Key point

In C++ templates, suitability is judged by use. Nothing in the template has to name a type or an interface in advance for this to work.

Where the Compiler Makes the Decision

The source describes this as a compile-time variant of duck typing. The checking is done by the compiler, before the program runs, and it is driven by the operations the template body performs. The compiler looks at what the definition does with its placeholder and asks whether the supplied argument supports those operations.

The diagram below traces a single instantiation. A template is instantiated when a specific argument type is supplied for its placeholder. At that moment the compiler compares the operations used in the body against what the argument supports. If every operation is supported, the instantiation is accepted. If one is not, the failure is reported by the compiler, and the unsupported operation is the place where the problem shows up.

Template definitionbody applies + to itsparameterArgument suppliedfor example int or BoxCompiler examinesbody operationsoperations are read fromthe definitionArgument supportsevery operation used?InstantiationacceptedCompile error atunsupported operationinstantiatecheckcompareyesno
When a template is instantiated with a specific argument type, the compiler checks whether that argument supports every operation the body performs. Suitability is decided by use, and a failure is tied to the operation that the argument cannot support.

Tracing a Template Through Compilation

The following example is an original illustration built to show the process in action. The template below performs one operation on its placeholder: it adds the parameter to itself. The definition never mentions int, double, or any class. It only states what it does with the value.

cpp
Notes on the code
  • The template body applies + to its placeholder; that operation is the requirement
  • Box has no + operation, so the second call fails at the point where the body uses it
Worked example

Following twice through two argument types

Decide whether the template twice is suitable for an argument of type int and for an argument of type Box, using only how the body uses its argument.

  1. 1
    Read the definition

    The body returns x + x. The only operation the definition performs on the placeholder is +.

  2. 2
    Supply the first argument type

    The call twice(3) instantiates the template with int. The compiler asks whether int supports the + the body uses. It does, so this instantiation is accepted.

  3. 3
    Supply the second argument type

    The call twice(Box{}) instantiates the template with Box. The compiler asks the same question. Box does not support +, so the check fails.

  4. 4
    Locate the failure

    The failure is tied to the operation in the body that Box cannot perform. No interface named in the declaration was consulted; the body itself defined the requirement.

Result

The template accepts int and rejects Box, and the reason is the single operation the body applies to its argument.

Timing of Definition, Argument, and Check

Put the pieces together in time order. A template definition is written with placeholders for arguments and contains operations applied to those placeholders. An argument value is then supplied. Because the checking is performed at compile time, the compiler examines the operations the definition uses and asks whether the supplied value supports them. The template's worth to a given argument depends on that answer, not on a list of requirements written out in advance.

Template definitionplaceholders and operationswrittenArgument valuesupplieda value is given for theplaceholderCompile-time checkbody operations comparedwith the valueSuitability judgedaccepted or rejectedthenthendecides
The template definition is written first, then an argument value is supplied, then the compiler checks the body's operations against that value. The suitability judgment happens at the check, before the program runs.

Value-Based and Type-Based Views

The more technical way to state the idea shifts attention from objects to values. A value is what gets operated on, and the presence and meaning of an operation depend solely on the operand values involved. The alternative view works in the other direction: objects have types, and those types determine which operations exist and what they mean.

In C++ objects such as variables work in a way that matches the object-type view. A value can live in an object, and only values that meet that object's requirements may be placed into it. Templates work differently. What happens at compile time when a template is used does not involve objects at all. It involves values. The comparison below sets the two views side by side.

Operation presencedepends on operand valuesOperation presencedecided by the object'stypeOperation meaningdepends on operand valuesOperation meaningdecided by the object'stypeTemplate checkingworks with values atcompile timeVariables in C++match this view
In the value-based view, an operation's presence and meaning follow from the operand values. In the type-based view, the object's type decides which operations exist and what they mean. Template checking at compile time follows the value-based view.
QuestionValue-based viewType-based view
What decides an operation?The operand values it is applied toThe object's type
Is the operation fixed in advance?No, its presence and meaning follow from the operandsTypes determine which operations exist and what they mean
Where does it govern C++ behavior here?Template checking at compile timeOrdinary objects such as variables

Two views of which operations apply

Mistakes When Reading Template Errors

  • Common mistake

    Looking for the requirements in the template declaration

    What it looks like

    Reading template<typename T> T twice(T x) and assuming T must provide a specific interface.

    Why it fails

    A template declaration does not list the operations an argument must support. The requirements come from how the definition uses the argument.

    Do this instead

    Read the body to find every operation applied to the placeholder, because those operations are the requirements.

  • Common mistake

    Deciding suitability by the argument's category

    What it looks like

    Assuming that a type is acceptable because it is a class, or rejected because it is a built-in number.

    Why it fails

    The question is not what category the argument belongs to, but whether it behaves the way the template needs.

    Do this instead

    Ask whether the argument supports each operation the body performs.

  • Common mistake

    Expecting the check to happen while the program runs

    What it looks like

    Believing that a bad argument will only be caught when the affected line executes.

    Why it fails

    The source describes the checking as compile-time. It is performed by the compiler before the program runs.

    Do this instead

    Expect a bad argument to be judged during compilation, and look for the reported problem at the operation the argument cannot support.

Testing Your Reasoning on a New Template

MEDIUM

Consider this template: template<typename T> void show(T x) { x.size(); }. Without running anything, decide whether show is suitable for an argument of type std::string and for an argument of type int. Justify each answer by naming the operation the body performs.

Hints
  • Start by identifying the operation the body applies to the placeholder x.
  • Ask whether each argument type supports that operation, not whether the type is a string or a number.
  • Remember that the declaration is silent; the body is the source of the requirement.

Key Takeaways

  • C++ templates use duck typing at compile time: an argument is judged suitable by how the template definition uses it, not by an interface listed in the declaration.
  • The checking happens when the definition uses the argument, so the operations in the body are the real requirements.
  • The value-based view says an operation's presence and meaning depend solely on its operand values, which matches how template checking works; the type-based view, which governs ordinary objects like variables, says object types decide which operations apply.
  • Read the template body, not just the declaration, to learn what an argument must support.

Related concepts

More concepts starting with T