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;
}