Concepts / Limitations of Template Programming

Limitations of Template Programming

The body of a template is the specification. To judge whether a type will work, read the body, list the operations it needs, and check that the type provides each one.

  • Programming

One Function for Many Types

Suppose you want one function that adds two values, whether they are integers, strings, or some type you wrote yourself. A C++ template lets you write that function once and have the compiler produce a version for each type it is used with. The practical question is how the compiler decides whether a particular type is acceptable for a template. This article answers that question and shows where the answer comes from.

The course source describes the rule as compile-time duck typing. Templates offer duck typing: a template argument is accepted based on the operations the template uses, not on a list of declared types. The check happens when the code is compiled, not while the program runs.

What do you think happens?

If a template is written to add two values with +, does the template need a list of approved types to accept an integer and a string?

  • Yes, the template header must name every allowed type

  • No, the template only needs the operations its body uses

Reveal answer
Answer:

No, the template only needs the operations its body uses

Templates are accepted by duck typing. The header does not list allowed types. What matters is whether each operation the body uses is valid for the type that is supplied.

Reading a Template as Requirements

Read a template as a list of requirements rather than as a list of types. Consider a template that adds two values of type T. The parameter type T appears in the signature, but nothing in the body says which types T may be. The body simply assumes that whatever T is, the two arguments can be added together.

cpp
Notes on the code
  • template <typename T> declares T as a placeholder for a type supplied at the call site
  • The body contains one expression, a + b, which names exactly one operation
  • The call add(2, 3) makes the compiler produce an int version of add
  • The call with std::string produces a string version of add
Key point

The body of a template is the specification. To judge whether a type will work, read the body, list the operations it needs, and check that the type provides each one.

Tracing the int and string Calls

Trace the two calls in main. For int, the expression a + b is valid, so the compiler produces an int version of add. For std::string, the expression a + b is also valid, because the string type provides concatenation through +, so the compiler produces a string version. Neither call required the template to know anything about the type's name. Only the operation mattered.

Worked example

Deciding whether add accepts a type

Does the template add accept the call add(x, y) when x and y are both std::string?

  1. 1
    Identify the operation in the body

    The body contains only a + b, so the single requirement is that + works on the two arguments.

  2. 2
    Substitute the type

    With T set to std::string, the body becomes std::string + std::string.

  3. 3
    Check the operation

    The string type provides concatenation through +, so the expression is valid.

  4. 4
    Decide

    Every operation the body needs is available, so the compiler produces a string version of add.

Result

The call is accepted, and the decision depended only on the operator +.

Following a Call Through the Body

The next case uses the same add template with a type the template cannot handle. The goal is not to memorize an error message but to see where the decision is made. Follow the argument from the call site, through the template body, to the point where the compiler must decide whether the body is valid for that argument.

Call add(a, b)argument type TInstantiate body forTsubstitute T into a + bIs a + b valid for T?Produce version for Tcall is acceptedCompile-time erroroperation missingyesno
When add is called with a type T, the compiler checks whether the body's operation a + b is valid for T. A yes leads to a compiled version for T. A no leads to a compile-time error.

The flowchart shows a single decision that is made for each argument type. The compiler never asks whether the type is on an approved list. It asks only whether the operation named in the body exists for that type. If it does, the version is produced. If it does not, the compile fails.

When Box Fails the Check

Now apply the same decision to a type called Box that the author wrote. For Box, the answer to the decision is no, because a + b is not defined for Box. The compiler therefore reports an error during compilation. Nothing about Box is wrong in itself. The template simply requires an operation that Box does not provide.

cpp
Notes on the code
  • Box is an ordinary struct with no operator+ defined
  • The call add(p, q) substitutes Box for T in the body
  • The compiler checks a + b for Box, finds no matching operation, and reports an error at compile time

Changing Box to provide operator+ would make the same call accepted. This shows that the acceptance rule depends on the operations available, not on a list of approved type names. The template header never changed, and neither did the call. Only the operations that Box provides changed.

Operations Decide, Not Declared Names

Duck typing gets its name from the saying that if something walks and quacks like a duck, it is treated as a duck. For templates, a type is treated as acceptable if it supports the operations the body uses. Compare this with a rule based on a declared list of types or on inheritance from a known base class. Those rules ask who the type is. Duck typing asks what the type can do.

QuestionDeclared-type ruleCompile-time duck typing
What decides acceptance?Whether the type appears on an approved listWhether the body's operations are valid for the type
Where is the rule written?In the template header or a separate listIn the template body
What does a new type need?To be added to the listThe operations the body uses
Example for BoxRejected unless listedRejected until operator+ is provided

Two ways a template could decide whether a type is acceptable

Mistakes Beginners Make

  • Common mistake

    Assuming the template header lists the allowed types

    What it looks like

    Reading template <typename T> and concluding that only the types named somewhere in the program can be used

    Why it fails

    The header only introduces the placeholder T. Nothing in the header says which types T may be.

    Do this instead

    Read the body and list the operations it needs, then check that the type provides each one.

  • Common mistake

    Assuming a type works because it worked for a different instantiation

    What it looks like

    Seeing add(2, 3) and add(std::string, std::string) compile, then expecting add(p, q) with two Box values to compile too

    Why it fails

    Each instantiation is checked separately for the type that is substituted. A type that compiles in one call can fail in another if the body uses an operator the second type does not define.

    Do this instead

    Check the operations the body uses against the specific type in each call.

  • Common mistake

    Assuming a missing operation only fails at runtime

    What it looks like

    Expecting the program to compile and then fail when add(p, q) runs

    Why it fails

    The compiler checks each instantiation when the template is compiled for a specific type, so an unsupported operation is reported as a compile-time error.

    Do this instead

    Expect the error at compile time and fix the missing operation, or choose a type that provides it.

Practice: Judge a New Type

EASY

The template below uses the same add function. Without running any code, decide whether a call add(p, q) with two values of a struct Pair compiles, where Pair has an int member and no operator+ defined. Then state which line of the template decides the answer.

Hints
  • Ask which operation the body needs for the type T
  • Check whether Pair provides that operation
  • The decision happens at the expression a + b, not at the template header
MEDIUM

Explain in one sentence why a type with no declared relationship to a list of approved types can still be accepted by a template.

Hints
  • Think about what the acceptance rule depends on
  • Compare the operations in the body with the operations the type provides

Practical Notes on Template Errors

Best practice

When a template call fails, start from the template body rather than the header. List each operation the body performs on its parameters, then check the supplied type against that list. This narrows the search to the operation that is actually missing.

Key Takeaways

  • A C++ template accepts a type based on the operations its body uses, not on a list of declared types. This is compile-time duck typing.
  • To judge a type, read the template body, list the operations it needs, and check that the type provides each one.
  • Each instantiation is checked when it is compiled for a specific type. An unsupported operation is reported as a compile-time error.
  • A type can work in one call and fail in another if the body uses an operator the second type does not define.
  • Providing the missing operation, such as operator+ for Box, makes the same call accepted without changing the template.

Related concepts

More concepts starting with L