How to Pass a Tuple with Variable Arguments as a Function Parameter in C++


Suppose we need to pass a tuple with a variable number of arguments as a parameter to a function.

C++11 introduced variadic templates, which allowed template definitions to take an arbitrary number of arguments of any type.

We can use ... to indicate a variadic template.

template <typename... args> 
void f(args... rest);
  • typename... args is our “template parameter pack”.
  • args... rest is how we unpack, or expand, this parameter pack.

Ideally, we should be able to run either statement below.

f(1, 2);
f(1, 2, 3, 4, 5);

Basics of Variadic Templates

Now, how can we group a variable number of arguments into a single argument?

Suppose we want to create a function that accepts any number of arguments and prints all of them.

Instead of using a loop, we’ll notice that variadic templates lend themselves nicely to recursive functions.

template<typename T>
void print(T last) {
  cout << last << endl;
}
template <typename T, typename... args> 
void print(T first, args... rest) {
    cout << first << endl; 
    print(rest...); 
}

The first argument is “peeled off” in T first, while the rest of the arguments are in args... rest. You’ll notice that rest gets smaller with each recursive call. We then have a base case, or a base function, that handles the last function call (when rest is empty).

We can compare this with a variadic adder function.

template<typename T>
T adder(T last) {
  return last;
}
template<typename T, typename... args>
T adder(T first, args... rest) {
  return first + adder(rest...);
}

These are both recursive functions that end when rest is empty.

We now see how rest, a single argument, can contain a variable number of arguments.

Variadic Templates with Tuples

We can use this knowledge to pass a variable number of arguments into a tuple that is then passed into a function.

Suppose we have two different tuples that we want to pass into our function f.

using Tuple1 = tuple<int32_t, bool, float, string>;
Tuple1 t1(10, true, 1.5f, "corgi");
f(t1);
using Tuple2 = tuple<float, bool>;
Tuple2 t2(5.4f, false);
f(t2);

We can use the parameter pack directly as the arguments for the tuple instead of the function.

template <typename... args> 
void f(tuple<args...> t) {
    // Print the 1st tuple element
    cout << get<0>(t) << endl;
}