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 template parameter pack is a template parameter that accepts zero or more template arguments. It serves as the foundational mechanism for variadic templates in C++, enabling the definition of classes, functions, and variables that can be instantiated with an arbitrary number of arguments. The syntax relies on the ellipsis operator (...) placed immediately before the identifier in the template parameter list.

Types of Template Parameter Packs

There are three distinct categories of template parameter packs, corresponding to the three types of template parameters: 1. Type Template Parameter Pack Accepts zero or more arbitrary types.
template <typename... Types>
struct TypePack;
2. Non-Type Template Parameter Pack Accepts zero or more constant expressions of a specific type.
template <int... Values>
struct NonTypePack;
3. Template Template Parameter Pack Accepts zero or more class templates.
template <template <typename> class... Templates>
struct TemplateTemplatePack;

Function Parameter Packs

A template parameter pack is frequently used in a function declaration to create a function parameter pack, which accepts zero or more function arguments. The ellipsis is placed between the template parameter pack name and the function parameter pack identifier.
template <typename... Args>
void functionName(Args... args); // 'Args' is the template pack, 'args' is the function pack

Pack Expansion

A parameter pack cannot be evaluated directly as a single entity; it must be expanded. Pack expansion consists of a pattern followed by an ellipsis (...). During instantiation, the compiler expands the pattern by replacing the pack with a comma-separated list of its constituent elements. Pack expansion can occur in several contexts, including function arguments, braced-init-lists, base class specifiers, and member initializer lists.
template <typename... T>
void targetFunction(T... args) {}

// 1. Base class specifier expansion
template <typename... Bases>
struct Derived : public Bases... { 
    
    // 2. Member initializer list expansion
    Derived(const Bases&... args) : Bases(args)... {} 
    
    void expand(Bases... args) {
        // 3. Function argument expansion
        targetFunction(args...); 
        targetFunction(&args...); 
        
        // 4. Braced-init-list expansion
        // A leading 0 is used to prevent an ill-formed empty array 
        // initialization if the parameter pack 'Bases' is empty.
        int sizes[] = { 0, sizeof(Bases)... }; 
    }
};

Fold Expressions (C++17)

Fold expressions provide a direct mechanism to reduce (fold) a parameter pack over a binary operator without requiring recursive template instantiations. They evaluate the pack using logical, arithmetic, or other binary operators. There are four types of fold expressions:
  • Unary Right Fold: (pack op ...) expands to E1 op (... op (En-1 op En))
  • Unary Left Fold: (... op pack) expands to ((E1 op E2) op ...) op En
  • Binary Right Fold: (pack op ... op init) expands to E1 op (... op (En-1 op (En op init)))
  • Binary Left Fold: (init op ... op pack) expands to (((init op E1) op E2) op ...) op En
Note on Empty Packs: Unary folds over most operators are ill-formed if the parameter pack is empty. Only && (defaults to true), || (defaults to false), and , (defaults to void()) permit empty packs in unary folds. For all other operators, a binary fold with an explicit initial value must be used to support zero arguments.
template <typename... Args>
auto sum(Args... args) {
    // Binary left fold: (((0 + arg0) + arg1) + ...)
    // Safe for zero arguments (returns 0).
    return (0 + ... + args); 
}

template <typename... Args>
bool allTrue(Args... args) {
    // Unary left fold: (((arg0 && arg1) && arg2) && ...)
    // Safe for zero arguments (returns true).
    return (... && args); 
}

The sizeof... Operator

To determine the number of elements contained within a parameter pack at compile time, C++ provides the sizeof... operator. It yields a std::size_t constant expression and can be applied to both template parameter packs and function parameter packs.
#include <cstddef>

template <typename... Args>
constexpr std::size_t getPackSize(Args... args) {
    constexpr std::size_t typeCount = sizeof...(Args); // Count of types
    constexpr std::size_t argCount = sizeof...(args);  // Count of function arguments
    return typeCount;
}

Placement Rules and Constraints

  • Primary Class Templates: The template parameter pack must be the final parameter in the template parameter list.
template <typename T, typename... Rest> // Valid
class MyClass;

template <typename... Rest, typename T> // Error
class MyClass;
  • Function Templates: A template parameter pack does not need to be the final parameter in the template parameter list, provided that all subsequent template parameters can be deduced from the function arguments or have default arguments. However, if a function parameter pack is not at the end of the function parameter list, it creates a non-deduced context for the parameter pack itself. While the trailing parameters can still be deduced, the compiler cannot implicitly deduce the pack, requiring explicit template arguments.
// Valid: T is deduced from the first argument, Args from the remaining arguments.
template <typename... Args, typename T>
void deduceFromArgs(T firstArg, Args... args); 

// Non-deduced context: The compiler cannot implicitly deduce the parameter pack 'Args'.
// Calling this requires explicitly specifying 'Args': invalidDeduction<int>(1, 2.0f);
// This explicitly sets Args to <int>, allowing T to be implicitly deduced as float.
template <typename... Args, typename T>
void invalidDeduction(Args... args, T finalArg); 
  • Class Template Partial Specializations: A parameter pack can appear anywhere in the specialization’s argument list, provided the compiler can unambiguously deduce it from the primary template’s arguments.
Master C++ with Deep Grasping Methodology!Learn More