Menu
Coddy logo textTech

Variadic Templates

Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 67 of 104.

What if you need a function that accepts any number of arguments of any types? Traditional templates require you to specify exactly how many type parameters you need. Variadic templates solve this by allowing templates to accept an arbitrary number of template arguments.

The syntax uses ... (ellipsis) to create a parameter pack:

template <typename... Args>
void printAll(Args... args) {
    // Args is a template parameter pack
    // args is a function parameter pack
}

To process the arguments, you typically use recursion with a base case. The parameter pack expands one argument at a time until none remain:

// Base case: no arguments left
void print() {
    std::cout << std::endl;
}

// Recursive case: process first argument, recurse with rest
template <typename T, typename... Rest>
void print(T first, Rest... rest) {
    std::cout << first << " ";
    print(rest...);  // Expand remaining arguments
}

print(1, 3.14, "hello", 'x');  // Output: 1 3.14 hello x

You can also use fold expressions (C++17) for simpler operations without explicit recursion:

template <typename... Args>
auto sum(Args... args) {
    return (args + ...);  // Fold expression: adds all arguments
}

std::cout << sum(1, 2, 3, 4) << std::endl;  // Output: 10

Variadic templates power many standard library features like std::make_unique, std::tuple, and std::function. They enable type-safe functions that work with any combination of argument types and counts.

challenge icon

Challenge

Easy

Let's build a flexible logging system that uses variadic templates to handle messages with any number of arguments. You'll create utilities that can concatenate values, count arguments, and print formatted output — all using parameter packs.

You'll organize your code across two files:

  • Variadic.h: Define your variadic template functions here.

    Create a printAll function that prints all its arguments separated by spaces, followed by a newline. Use the recursive approach with a base case that prints just a newline, and a recursive case that prints the first argument, a space, then recurses with the remaining arguments.

    Create a countArgs function that returns the number of arguments passed to it. Use sizeof... to get the size of the parameter pack.

    Create a sum function that adds all numeric arguments together and returns the result. Use a fold expression with the + operator.

    Create a product function that multiplies all numeric arguments together and returns the result. Use a fold expression with the * operator.

  • main.cpp: Read four inputs (each on a separate line):
    1. An integer
    2. A double
    3. A string
    4. A character

    Demonstrate your variadic templates by:

    1. Calling printAll with all four inputs and printing the result
    2. Printing the count of arguments when calling countArgs with the integer, double, and character: Argument count: <count>
    3. Calculating the sum of the integer, the double, and the values 10 and 5, then printing: Sum: <result>
    4. Calculating the product of the integer and the values 2 and 3, then printing: Product: <result>
    5. Calling printAll with just the string (single argument)
    6. Calling printAll with no arguments (should print just a newline)

For example, with inputs 5, 2.5, Hello, and X:

5 2.5 Hello X 
Argument count: 3
Sum: 22.5
Product: 30
Hello 

Notice how printAll handles any number of arguments of mixed types, from four arguments down to zero. The sum and product functions use fold expressions to elegantly combine all values without explicit recursion. Your countArgs function demonstrates how sizeof... gives you the pack size at compile time.

Cheat sheet

Variadic templates allow templates to accept an arbitrary number of template arguments using the ... (ellipsis) syntax to create a parameter pack:

template <typename... Args>
void printAll(Args... args) {
    // Args is a template parameter pack
    // args is a function parameter pack
}

Process arguments using recursion with a base case:

// Base case: no arguments left
void print() {
    std::cout << std::endl;
}

// Recursive case: process first argument, recurse with rest
template <typename T, typename... Rest>
void print(T first, Rest... rest) {
    std::cout << first << " ";
    print(rest...);  // Expand remaining arguments
}

print(1, 3.14, "hello", 'x');  // Output: 1 3.14 hello x

Use sizeof... to get the number of arguments in a parameter pack:

template <typename... Args>
size_t countArgs(Args... args) {
    return sizeof...(args);
}

Use fold expressions (C++17) for simpler operations without explicit recursion:

template <typename... Args>
auto sum(Args... args) {
    return (args + ...);  // Fold expression: adds all arguments
}

std::cout << sum(1, 2, 3, 4) << std::endl;  // Output: 10

Fold expressions work with other operators like multiplication:

template <typename... Args>
auto product(Args... args) {
    return (args * ...);  // Multiplies all arguments
}

Try it yourself

#include <iostream>
#include <string>
#include "Variadic.h"

using namespace std;

int main() {
    // Read inputs
    int intVal;
    double doubleVal;
    string strVal;
    char charVal;
    
    cin >> intVal;
    cin >> doubleVal;
    cin >> strVal;
    cin >> charVal;
    
    // TODO: Call printAll with all four inputs
    
    // TODO: Print argument count using countArgs with intVal, doubleVal, and charVal
    // Format: "Argument count: <count>"
    
    // TODO: Calculate and print sum of intVal, doubleVal, 10, and 5
    // Format: "Sum: <result>"
    
    // TODO: Calculate and print product of intVal, 2, and 3
    // Format: "Product: <result>"
    
    // TODO: Call printAll with just the string
    
    // TODO: Call printAll with no arguments
    
    return 0;
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming