Variadic Template / Function Arguments

Variadic functions are those that accept a variable number of arguments.

C++

Resources

Variadic Template

A variadic template is a class or function template that supports an arbitrary number of argument.

template<typename... Args> // parameter pack -? ... Args
void f(Args... args) {} // pack expansion -> pattern: Args
  • A parameter pack is introduced by an identifier Args prefixed by an ellipsis…
  • A parameter pack can later be expanded by an ellipsis …
    • A pack expansion is equivalent to a comma-separated list of instances of the pattern

A pattern is a set of tokens containing the identifiers of one or more parameter packs.

Resources

Example of useful variadic template class:

  • Tuple (can hold multiple types)

But most of the time, seems to be used for variadic funtions.

Basic example (from Modern C++)

template<typename T, typename R>
auto add(T a, R b) {
    return a + b;
}
 
template<typename T, typename... Args> 
auto add(T a, Args... args) { 
    return a + add(args...);
}

Since C++17, you can do this

Unary/Binary folding
template<typename... Args>
auto add_unary(Args... args) { // Unary folding
    return (... + args); // unfold: 1 + 2.0f + 3ull
}
 
template<typename... Args>
auto add_binary(Args... args) { // Binary folding
    return (1 + ... + args); // unfold: 1 + 1 + 2.0f + 3ull
}
 
add_unary(1, 2.0f, 3ll); // returns 6.0f (float)
add_binary(1, 2.0f, 3ll); // returns 7.0f (float)

The number of variadic arguments can be retrieved with the sizeof… operator

sizeof...(args) // e.g. 3

For example, when you use make_shared and you pass in the constructor arguments,

template <typename T, typename... Args>
std::shared_ptr<T> make_shared(Args&&... args) {
    // Allocate and construct an object of type T using the provided arguments.
    return std::shared_ptr<T>(new T(std::forward<Args>(args)...));
}
Minor quirks

When a pattern contains more than one parameter pack, all packs must have the same length.

  • Though you probably will never run into this
template<typename... A, typename... B>
void foo(A... a, B... b) {
    // Code that tries to use both parameter packs together
}

C-Style Variadic

There’s another way, which I found out through chatgpt which is to use <cstdarg> (this is c-style variadic functions)

#include <iostream>
#include <cstdarg>
 
void variadicFunction(int num, ...) {
    va_list args;
    va_start(args, num);
 
    for (int i = 0; i < num; ++i) {
        int value = va_arg(args, int);  // Get next argument, specifying the type
        std::cout << value << std::endl;
    }
 
    va_end(args);
}
 
int main() {
    variadicFunction(3, 10, 20, 30);  // First argument is the count of subsequent arguments
    return 0;
}
 

Python

In Python, this is done using the *args and **kwargs.

def variadic_function(*args):
    for arg in args:
        print(arg)
 
variadic_function(1, 2, 3)
def variadic_function(*args, **kwargs):
    print("Positional arguments:", args)
    print("Keyword arguments:", kwargs)
 
variadic_function(1, 2, 3, name="Alice", age=30)

The kwargs is used for those with keyword arguments

Positional arguments: (1, 2, 3)
Keyword arguments: {'name': 'Alice', 'age': 30}