Advanced 13 min

Variadic Templates

Master variadic templates to create functions and classes that accept any number of arguments with full type safety

Learn variadic templates - the powerful C++11 feature that enables type-safe functions accepting any number of arguments of any types.

A Simple Example

#include <iostream>

// Base case: stop recursion
void print() {
    std::cout << "\n";
}

// Variadic template: handles any number of arguments
template <typename T, typename... Args>
void print(T first, Args... rest) {
    std::cout << first << " ";
    print(rest...);  // Recursive call with remaining arguments
}

int main() {
    print(1, 2, 3);                          // 1 2 3
    print("Hello", 42, 3.14, "World");      // Hello 42 3.14 World
    print("Single");                         // Single

    return 0;
}

Breaking It Down

Parameter Packs typename... Args

  • Syntax: typename... Args declares a template parameter pack
  • Usage: Args... args declares a function parameter pack
  • Represents zero or more template arguments of any types
  • Remember: The ... goes after typename for the type, before the name for expansion

Pack Expansion args...

  • Syntax: args... expands the parameter pack
  • Example: print(rest...) expands to print(arg1, arg2, arg3...)
  • Works with expressions: func(process(args)...) applies process to each argument
  • Remember: The ... goes after the pack name for expansion

Recursive Template Pattern

  • Pattern: Peel off first argument, recurse with remaining arguments
  • Base case: Function with no parameters or single parameter
  • Each recursion: Compiler instantiates a new function template
  • Remember: Without a base case, you get infinite recursion and compilation error

sizeof...(args) Operator

  • What it does: Returns number of arguments in parameter pack at compile time
  • Usage: sizeof...(Args) or sizeof...(args)
  • Returns: Compile-time constant - no runtime overhead
  • Remember: Different from sizeof() which returns byte size

Why This Matters

  • Variadic templates power essential modern C++ features you use every day.
  • They enable std::tuple, std::bind, std::thread constructors, std::make_unique, and perfect forwarding.
  • Understanding them opens the door to creating flexible, type-safe APIs that feel natural to use.
  • They replace dangerous old-style variadic functions like printf with compile-time type checking.

Critical Insight

Variadic templates do not create loops at runtime - they generate multiple function instantiations at compile time. When you call print(1, 2, 3), the compiler generates three functions: one for (int, int, int), one for (int, int), and one for (int), plus the base case.

It is like unrolling a loop at compile time. The compiler does all the work ahead of time, generating optimized code for each specific case. By runtime, everything is already specialized and optimized - zero overhead!

Best Practices

Always provide a base case for recursive variadic templates: Without it, you get compilation errors from infinite recursion.

Use sizeof...(args) to query parameter pack size: This is a compile-time constant with zero runtime cost.

Combine with perfect forwarding: Use Args&&... args with std::forward<Args>(args)... for efficient argument passing.

Consider fold expressions (C++17): For simple operations, fold expressions are cleaner than recursion.

Common Mistakes

Forgetting the base case: Recursive variadic templates need a base case or you get infinite recursion at compile time.

Pack expansion syntax confusion: Use args... not ...args for pack expansion.

Assuming runtime loops: Variadic templates create multiple functions at compile time, not runtime loops.

Forgetting perfect forwarding: Use Args&&... and std::forward<Args>(args)... to avoid unnecessary copies.

Debug Challenge

This variadic template is missing the base case. Click the highlighted line to see what needs to be added:

1 #include <iostream>
2
3 // Missing base case!
4
5 template <typename T, typename... Args>
6 void print(T first, Args... rest) {
7 std::cout << first << " ";
8 print(rest...);
9 }

Quick Quiz

  1. What does sizeof...(args) return?
The number of arguments in the parameter pack
The size in bytes of all arguments
The size of the first argument
  1. When are variadic template functions instantiated?
At compile time for each unique argument list
At runtime when called
Once for all argument combinations
  1. What happens if you forget the base case in recursive variadic templates?
Compilation error due to infinite recursion
Runtime error
It works fine

Practice Playground

Time to try out what you just learned! Play with the example code below, experiment by making changes and running the code to deepen your understanding.

Lesson Progress

  • Fix This Code
  • Quick Quiz
  • Practice Playground - run once