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.
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.
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.
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.
- 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
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.
- 1Read the definition
The body returns x + x. The only operation the definition performs on the placeholder is +.
- 2Supply 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. - 3Supply 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. - 4Locate 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.
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.
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.
| Question | Value-based view | Type-based view |
|---|---|---|
| What decides an operation? | The operand values it is applied to | The object's type |
| Is the operation fixed in advance? | No, its presence and meaning follow from the operands | Types determine which operations exist and what they mean |
| Where does it govern C++ behavior here? | Template checking at compile time | Ordinary 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 likeReading template<typename T> T twice(T x) and assuming T must provide a specific interface.
Why it failsA template declaration does not list the operations an argument must support. The requirements come from how the definition uses the argument.
Do this insteadRead 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 likeAssuming that a type is acceptable because it is a class, or rejected because it is a built-in number.
Why it failsThe question is not what category the argument belongs to, but whether it behaves the way the template needs.
Do this insteadAsk whether the argument supports each operation the body performs.
Common mistake Expecting the check to happen while the program runs
What it looks likeBelieving that a bad argument will only be caught when the affected line executes.
Why it failsThe source describes the checking as compile-time. It is performed by the compiler before the program runs.
Do this insteadExpect 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
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.