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 size of the first argument
The number of arguments in the parameter pack
The maximum number of arguments allowed
The size in bytes of all arguments
  1. When are variadic template functions instantiated?
At runtime when called
Only when explicitly requested
At compile time for each unique argument list
Once for all argument combinations
  1. What happens if you forget the base case in recursive variadic templates?
Runtime error
The function returns nullptr
It works fine
Compilation error due to infinite recursion

Step Through the Code

Walk through the code step by step. Watch how variables change and see the program output at each line.

Lesson Progress

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