Skip to main content
A variadic parameter is a language mechanism that allows a function, template, or macro to accept an arbitrary number of arguments. C++ supports two distinct implementations: compile-time variadic templates (introduced in C++11) and runtime C-style variadic functions.

Variadic Templates (Parameter Packs)

Variadic templates utilize an ellipsis (...) to declare a parameter pack. The compiler resolves these at compile time, ensuring strict type safety and generating specific function signatures for each unique invocation. There are two distinct types of parameter packs:
  1. Template parameter pack: Represents zero or more template parameters (types, non-types, or templates).
  2. Function parameter pack: Represents zero or more function parameters.

Pack Expansion

Parameter packs cannot be indexed or accessed directly; they must be expanded. The compiler expands the pack by replacing it with a comma-separated list of its instantiated elements. Historically, this required recursive template instantiation, peeling off one argument at a time until a base case was reached.

Perfect Forwarding

When a function parameter pack is declared as a forwarding reference (Args&&...), it can be combined with std::forward to perfectly forward an arbitrary number of arguments. The expansion std::forward<Args>(args)... preserves the exact value category (lvalue or rvalue) and const/volatile qualifiers of every argument in the pack.

Fold Expressions (C++17)

C++17 introduced fold expressions, which allow a parameter pack to be reduced over a binary operator without recursive instantiation. The compiler expands the expression directly into a sequence of operations. A critical language rule applies to empty parameter packs: unary folds over most operators (including +, -, *, /) are ill-formed and will trigger a compilation error if the pack is empty. Only &&, ||, and , have default values for empty unary folds. To safely handle potentially empty packs with other operators, a binary fold must be used to provide an explicit initial value.

The sizeof... Operator

The sizeof... operator queries the number of elements contained within a parameter pack. It evaluates at compile time and returns a std::size_t constant expression.

C-Style Variadic Functions

Inherited from C, this legacy mechanism uses the ellipsis (...) at the end of a function parameter list. It bypasses C++ compile-time type checking and relies on runtime stack manipulation via macros defined in the <cstdarg> header.

Mechanics and Limitations

  • Type Erasure: The compiler does not enforce type safety for arguments passed to the ellipsis. The receiving function must deduce types implicitly (e.g., via a format string) or assume a specific type.
  • Default Argument Promotions: Arguments passed through C-style variadics undergo implicit promotion. For example, float is promoted to double, and narrow integer types (char, short) are promoted to int or unsigned int.
  • Object Restrictions: Passing non-trivial class types (e.g., std::string, std::vector) to an ellipsis is conditionally-supported with implementation-defined semantics (since C++11), or results in undefined behavior (pre-C++11). The C++ standard does not guarantee that constructors or destructors will be invoked correctly through this mechanism.
Tired of Poor C++ Skills? Fix That With Deep Grasping!Learn More