Templates Deep

By the end of this lesson you'll be able to write functions that take any number of arguments, customise a template for one specific type, constrain templates so misuse fails with a clear message, and reach for CRTP and C++20 concepts — the techniques the whole Standard Library is built on.

Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.

Part of the free C++ course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

Think of a template as a cookie cutter , and a parameter pack as a recipe that says "add as many toppings as the customer asks for". A fold expression is the single motion that stirs every topping in at once. Specialization is keeping one special cutter just for, say, gingerbread men, because the generic round cutter would ruin them. And concepts are the sign on the counter — "dough only, no rocks" — so an impossible order is refused politely at the door instead of jamming the machine deep inside. The machine only actually runs (compiles) when a real order comes in, which is why template errors show up at the moment of use , not when the recipe was written.

1. Variadic Templates & Fold Expressions

A variadic template accepts any number of arguments. You declare a parameter pack with typename... Args (the pack of types ) and a matching Args... args (the pack of values ). You can't index a pack directly — you expand it. The modern way (C++17) is a fold expression : (args + ...) applies + across the whole pack in one line. sizeof...(args) tells you how many items are in the pack, at compile time. Read this worked sum(...) , run it, then you'll write your own.

Your turn. The program below is almost complete — fill in the two blanks marked ___ using the hints in the comments. One is an arithmetic fold, the other is a comma fold cout args , ... that runs a statement once per item.

2. Specialization, Type Traits & SFINAE

Full specialization ( template struct Describe bool ) replaces the template for one exact type. Partial specialization ( struct Describe T* ) replaces it for a whole family — here, every pointer type. The header type_traits gives you compile-time questions about types, like is_integral_v T . SFINAE ("Substitution Failure Is Not An Error") with enable_if_t makes an overload simply not exist when its condition is false, so the compiler quietly picks the right one instead of compiling something wrong.

Remember the rule: only class/struct templates can be partially specialized . For functions , you write ordinary overloads (or use a concept) instead of a partial specialization.

3. CRTP & C++20 Concepts

CRTP (Curiously Recurring Template Pattern) is a class that inherits from a base templated on itself : struct Dog : Greeter Dog . The base can then call the derived class's methods with zero runtime cost — "static polymorphism", resolved entirely at compile time, no virtual table. Concepts (C++20) are named, readable constraints on types: concept Addable = requires(T a, T b) {' a + b; '}; . Constrain a template with the concept name and misuse fails with a short, clear message instead of a wall of template errors.

Now you try. Define a Numeric concept from a type trait, then use it to constrain square() so only number types compile. Fill in the two blanks:

A template is not fully checked when you write it — only when you use it with a concrete type. That moment is called instantiation . So a mistake inside a template appears at the call site that triggered it, often buried deep in the compiler's output.

Two habits tame this. Add a static_assert with a message at the top of a template to fail early and clearly. Better still, in C++20 attach a concept — the compiler then says "constraint not satisfied" right at the call, instead of erroring 40 lines deep inside the body.

No blanks this time — just a brief and an outline. Write a variadic countOver(threshold, values...) that returns how many values beat the threshold, using a comma fold over a counter. Check your output against the example in the comments.

Practice quiz

What does template <typename... Args> declare?

  • A single template type
  • A variadic macro
  • A parameter pack — a name standing for zero or more template arguments
  • An array of types

Answer: A parameter pack — a name standing for zero or more template arguments. typename... Args declares a type parameter pack: it stands for zero or more types.

How do you work with a parameter pack?

  • Expand it (with ... or a fold) or count it with sizeof...

Answer: Expand it (with ... or a fold) or count it with sizeof.... You never index a pack directly — you expand it (... or a fold) or count it with sizeof...(Args).

What does the fold expression (args + ...) do?

  • Adds 1 to every argument
  • Appends args to a list
  • Returns the number of arguments
  • Collapses the whole pack with the + operator in one line

Answer: Collapses the whole pack with the + operator in one line. A fold expression like (args + ...) applies + across the entire pack, e.g. a1 + (a2 + (a3 + ...)).

What does sizeof...(args) return?

  • The total bytes of all arguments
  • The number of elements in the parameter pack, at compile time
  • The size of the largest argument
  • Always 1

Answer: The number of elements in the parameter pack, at compile time. sizeof...(pack) is the count of items in the pack, computed at compile time (not a byte size).

What is the difference between full and partial specialization?

  • Full pins every parameter to one exact type; partial fixes a family/pattern like T*
  • Full fixes a pattern; partial fixes one exact type
  • They are the same thing
  • Partial only works on functions

Answer: Full pins every parameter to one exact type; partial fixes a family/pattern like T*. Full specialization (template<> Describe<bool>) pins one exact type; partial (Describe<T*>) covers a family.

Which kinds of templates can be PARTIALLY specialized?

  • Only function templates
  • Any template
  • Only class/struct templates — for functions you overload instead
  • Only variable templates

Answer: Only class/struct templates — for functions you overload instead. Partial specialization is class/struct-only; functions are overloaded (or constrained) rather than partially specialized.

What does SFINAE stand for, and what does enable_if exploit it for?

  • Standard Function In Namespace And Enum; to rename functions
  • Substitution Failure Is Not An Error; to make an overload simply not exist when a condition is false
  • Static Functions Are Never In Errors; to inline functions
  • Single Format Is Not Always Easy; to format types

Answer: Substitution Failure Is Not An Error; to make an overload simply not exist when a condition is false. SFINAE = Substitution Failure Is Not An Error; enable_if uses it so an overload drops out when its condition is false.

Which <type_traits> helper is true only for integer types?

  • is_floating_point_v<T>
  • is_pointer_v<T>
  • is_class_v<T>
  • is_integral_v<T>

Answer: is_integral_v<T>. is_integral_v<T> is true for integer types (and false for double); is_floating_point_v<T> is the float counterpart.

What does CRTP (Curiously Recurring Template Pattern) provide?

  • Runtime polymorphism via virtual tables
  • Static (compile-time) polymorphism with no virtual-table cost
  • Automatic memory management
  • Variadic argument handling

Answer: Static (compile-time) polymorphism with no virtual-table cost. CRTP has a base templated on the derived type, so calls resolve at compile time with no virtual-table overhead.

Why do template errors often point at the call site rather than the template definition?

  • The compiler has a bug
  • Templates are never checked
  • A template is only fully type-checked when it is instantiated with a concrete type
  • Errors always point at line 1

Answer: A template is only fully type-checked when it is instantiated with a concrete type. Templates are checked at instantiation, so a mistake inside surfaces at the call that triggered it; concepts make this clearer.