SyntaxStudy
Sign Up
C++ Variadic Templates and Parameter Packs
C++ Beginner 1 min read

Variadic Templates and Parameter Packs

Variadic templates, introduced in C++11, allow templates to accept any number of type or non-type arguments through parameter packs. The ellipsis '...' syntax marks a parameter pack, and pack expansion 'pack...' generates a comma-separated list of expressions by applying a pattern to each element. This feature underpins 'std::tuple', 'std::make_unique', perfect forwarding utilities, and many other standard library components. Recursive variadic templates process parameter packs by peeling off one argument at a time. A base case handles the empty-pack termination. C++17 fold expressions provide a more concise alternative for common operations: '(args + ...)' sums all arguments without needing explicit recursion, and '(os << ... << args)' streams all arguments to an output stream. Perfect forwarding combines variadic templates with 'std::forward' to pass arguments to inner functions preserving their value category — lvalue or rvalue. This enables factory functions like 'std::make_unique' to construct objects with arbitrary constructor arguments without unnecessary copies, regardless of whether the arguments are temporaries or named variables.
Example
#include <iostream>
#include <string>
#include <tuple>

// C++17 fold expression: sum any number of values
template<typename... Args>
auto sum(Args&&... args) {
    return (args + ...);   // fold over +
}

// Variadic print using fold over comma operator
template<typename... Args>
void printAll(Args&&... args) {
    ((std::cout << args << " "), ...);
    std::cout << "\n";
}

// Recursive variadic: count arguments
template<typename... Args>
constexpr std::size_t countArgs(Args&&...) {
    return sizeof...(Args);
}

// Perfect-forwarding factory
template<typename T, typename... Args>
T* createRaw(Args&&... args) {
    return new T(std::forward<Args>(args)...);
}

struct Point {
    Point(double x, double y, double z)
        : x(x), y(y), z(z) {}
    double x, y, z;
    void print() const {
        std::cout << "Point(" << x << "," << y << "," << z << ")\n";
    }
};

int main() {
    std::cout << sum(1, 2, 3, 4, 5)         << "\n";  // 15
    std::cout << sum(1.5, 2.5, 3.0)         << "\n";  // 7.0
    printAll("Hello", 42, 3.14, std::string("world"));
    std::cout << countArgs(1,'a',"hi",3.14) << "\n";  // 4

    Point* p = createRaw<Point>(1.0, 2.0, 3.0);
    p->print();
    delete p;

    // std::tuple uses variadic templates internally
    auto t = std::make_tuple(1, std::string("hi"), 3.14);
    std::cout << std::get<0>(t) << " " << std::get<1>(t) << "\n";

    return 0;
}