Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A variadic template is a class, function, alias (C++11), or variable (C++14) template that accepts an arbitrary number of template arguments. This is achieved through the use of a parameter pack, which represents a comma-separated list of zero or more arguments. The ellipsis operator (...) is the core syntactic element used to declare and expand parameter packs. Its placement determines whether it is defining a pack or expanding one.

Supported Template Types

Variadic templates can be applied across four distinct template categories:
#include <tuple>
#include <cstddef>

// 1. Class Template
template <typename... Ts>
class Tuple {};

// 2. Function Template
template <typename... Ts>
void process(Ts... args) {}

// 3. Alias Template (C++11)
template <typename... Ts>
using my_tuple = std::tuple<Ts...>;

// 4. Variable Template (C++14)
template <typename... Ts>
constexpr std::size_t size_v = sizeof...(Ts);

Core Terminology and Syntax

  1. Template Parameter Pack: Declared in the template parameter list. It represents zero or more types (or non-types/templates).
  2. Function Parameter Pack: Declared in the function signature. It represents zero or more function arguments.
  3. Pack Expansion: The process of unpacking the parameter pack into separate, comma-separated arguments.
// 'Types' is a template parameter pack
template <typename... Types> 
// 'args' is a function parameter pack
void evaluate(Types... args) { 
    // 'args...' is a pack expansion
    targetFunction(args...); 
}

Pack Expansion Mechanics

When a pack is expanded, the compiler duplicates the pattern preceding the ellipsis for each element in the pack, separating them with commas.
#include <utility>

void consume(int, double); // Example target function

template <typename... Args>
void forward_all(Args&&... args) {
    // The pattern is 'std::forward<Args>(args)'
    // If Args contains {int, double}, this expands to:
    // consume(std::forward<int>(args1), std::forward<double>(args2));
    consume(std::forward<Args>(args)...); 
}

Processing Parameter Packs

Because parameter packs cannot be indexed directly (e.g., args[0]), C++ provides specific mechanisms to iterate over or process the elements within a pack.

1. Recursive Instantiation (Pre-C++17)

The traditional method for processing a pack involves compile-time recursion. This requires two overloads: a base case to terminate the recursion, and a variadic template that isolates the first argument and recursively passes the remaining pack.
void handle(int); // Example target function

// 1. Base case: invoked when the parameter pack is empty
void process() {}

// 2. Recursive step: peels off the first argument (Head) 
// and leaves the rest in the pack (Tail)
template <typename Head, typename... Tail>
void process(Head head, Tail... tail) {
    handle(head);      // Process the isolated argument
    process(tail...);  // Recursively expand the remaining pack
}

2. Fold Expressions (C++17)

C++17 introduced fold expressions, which apply a binary operator to all elements of a parameter pack directly, eliminating the need for recursive boilerplate. There are four types of fold expressions:
  • Unary Right Fold: (pack op ...) expands to arg1 op (arg2 op arg3)
  • Unary Left Fold: (... op pack) expands to (arg1 op arg2) op arg3
  • Binary Right Fold: (pack op ... op init) expands to arg1 op (arg2 op (arg3 op init))
  • Binary Left Fold: (init op ... op pack) expands to ((init op arg1) op arg2) op arg3
#include <iostream>

template <typename... Args>
bool all_true(Args... args) {
    // Unary left fold using the logical AND operator
    return (... && args); 
}

template <typename... Args>
void print_all(Args... args) {
    // Unary right fold using the comma operator.
    // The pattern being expanded is 'void(std::cout << args << " ")'
    (void(std::cout << args << " "), ...); 
}

Querying Pack Size

The sizeof... operator is used to determine the number of elements contained within a parameter pack at compile time. It yields a constant expression of type std::size_t.
#include <cstddef>

template <typename... Args>
constexpr std::size_t get_argument_count() {
    return sizeof...(Args); // Returns the number of types in the pack
}
Master C++ with Deep Grasping Methodology!Learn More