From C++11, is an incredible expansion to , that offers a fixed-size collection of heterogeneous values. Unfortunately, tuples can be somewhat dubious to manage in a conventional fashion. But, subsequently released C++ standard introduced a few features & helpers that greatly reduce the necessary boilerplate. So, in this article, I will explain the variadic template in C++ with the help of unsophisticated tuple implementation. And also walks you through a tricky part of tuple i.e. loop through tuple element. In spite of the fact that I have shrouded the variadic template in my prior article i.e. . So, my focus here would be a blend of variadic template & tuple implementation with more up to date C++ gauges. std::tuple Modern C++ C++ Template: A Quick UpToDate Look Motivation It is often useful to define class/struct/union/function that accepts a variable number and type of arguments. If you have already used C you’ll know that printf function can accept any number of arguments. Such functions are entirely implemented through macros or . And because of that it has several disadvantages like , cannot accept references as arguments, etc. ellipses operator type-safety Variadic Class Template: Implementing Tuple Class So, let’s build our own same as with the help of variadic template. ADT std::tuple The variadic template in C++ usually starts with the general (empty) definition, that also serves as the base-case for template recursion termination in the later specialisation: < ... T> }; template typename { struct Tuple This already allows us to define an empty structure i.e. Tuple<> object;, albeit that isn’t very useful yet. Next comes the recursive case specialisation: < T, ... Rest > T first; Tuple<Rest...> rest; Tuple( T& f, Rest& ... r) : first(f) , rest(r...) { } }; Tuple< > t1( ); Tuple< , , > t2( , , ); template typename typename // Template parameter pack <T, Rest...> { struct Tuple // Class parameter pack // Parameter pack expansion const const bool false // Case 1 int char string 1 'a' "ABC" // Case 2 How Does Variadic Class Template Works? To understand variadic class template, consider use case 2 above i.e. Tuple< , , > t2( , , ); int char string 1 'a' "ABC" The declaration first matches against the specialization, yielding a structure with int first; and Tuple<char, string> rest; data members. The rest definition again matches with specialization, yielding a structure with char first; and Tuple<string> rest; data members. The rest definition again matches this specialization, creating its own string first; and Tuple<> rest; members. Finally, this last rest matches against the base-case definition, producing an empty structure. You can visualize this as follows: < , , > -> first -> < , > rest -> first -> < > rest -> first -> <> rest -> (empty) Tuple int char string int Tuple char string char Tuple string string Tuple Variadic Function Template: Implementing get<>() Function for Tuple Class So, far we have designed data structure with variable number and type of data members. But still, it isn’t useful as there is no mechanism to retrieve data from our Tuple class. So let’s design one: < idx, < ...> GetHelper<idx, Tuple<Args...>>::get(t); } template size_t template typename , ... > ( <Args...> & ) { class Tuple typename Args auto get Tuple t return As you can see this get function is templatized on the idx. So usage can be like get<1>(t), similar to . Though, the actual work is done by a static function in a helper class i.e. GetHelper. std::tuple Note also the use of a C++14-style auto return type that makes our lives significantly simpler as otherwise, we would need quite a complicated expression for the return type. So, on to the helper class. This time we will need an empty forward declaration and two specializations. First the empty declaration: < idx, T > template size_t typename ; struct GetHelper Now the base-case (when idx==0). In this specialisation, we just return the first member: < T, ... Rest > { data.first; } }; template typename typename <0, Tuple<T, Rest...>> { struct GetHelper T static get (Tuple<T, Rest...> &data) return In the recursive case, we decrement idx and invoke the GetHelper for the rest member: < idx, T, ... Rest > { GetHelper<idx - , Tuple<Rest...>>::get(data.rest); } }; template size_t typename typename <idx, Tuple<T, Rest...>> { struct GetHelper static auto get (Tuple<T, Rest...> &data) return 1 To work through an example, suppose we have Tuple data and we need get<1>(data). This invokes GetHelper<1, Tuple<T, Rest...>>>::get(data) (the 2nd specialization). Which in turn invokes GetHelper<0, Tuple<T, Rest...>>>::get(data.rest). And finally returns (by the 1st specialization as now idx is 0) data.rest.first. So that’s it! Here is the whole functioning code, with some example use in the main function: < idx, T > }; < ... T> }; < T, ... Rest > { data.first; } }; < idx, T, ... Rest > { GetHelper<idx - , Tuple<Rest...>>::get(data.rest); } }; < T, ... Rest > T first; Tuple<Rest...> rest; Tuple( T &f, Rest &... r) : first(f) , rest(r...) { } }; < idx, < ...> GetHelper<idx, Tuple<Args...>>::get(t); } { Tuple< , , > t( , , ); << get< >(t) << ; ; } // Forward Declaration & Base Case ----------------------------------------- template size_t typename { struct GetHelper template typename { struct Tuple // ------------------------------------------------------------------------- // GetHelper --------------------------------------------------------------- template typename typename <0, Tuple<T, Rest...>> { struct GetHelper // Specialization for index 0 T static get (Tuple<T, Rest...> &data) return template size_t typename typename <idx, Tuple<T, Rest...>> { struct GetHelper // GetHelper Implementation static auto get (Tuple<T, Rest...> &data) return 1 // ------------------------------------------------------------------------- // Tuple Implementation ---------------------------------------------------- template typename typename <T, Rest...> { struct Tuple const const // ------------------------------------------------------------------------- // get Implementation ------------------------------------------------------ template size_t template typename , ... > ( <Args...> & ) { class Tuple typename Args auto get Tuple t return // ------------------------------------------------------------------------- int main () int char string 500 'a' "ABC" cout 1 endl return 0 Variadic Template vs Fold Expression There is two way to process C++ parameter pack i.e. 1. Recursion 2. Fold Expression(From C++17) At whatever point conceivable, we should process a parameter pack with fold expression instead of using recursion. Because it has some benefits as: 1. Less code to write 2. Faster code (without optimizations), as you just have a single expression instead of multiple function calls 3. Faster to compile, as you deal with fewer template instantiation Processing a Parameter Pack With Recursion As we have seen earlier, variadic template starts with empty definition i.e. base case for recursion. {} void print () Then the recursive case specialisation: < First, ... Rest > { << first << ; print(rest...); } template typename typename // Template parameter pack void print (First first, Rest... rest) // Function parameter pack cout endl // Parameter pack expansion This is now sufficient for us to use the print function with variable number and type of arguments. For example: print( , , ); 500 'a' "ABC" Processing a Parameter Pack With Fold Expression < ... Args> { ( ( << args << ), ...); } template typename void print (Args... args) void cout endl See, no cryptic boilerplate required. Isn’t this solution looks neater? There are total 3 types of folding: Unary fold, Binary fold & Fold over a comma. Here we have done left folding over a comma. You can read more about Fold Expression . here Loop-Through/Iterate Over Tuple Elements in C++ If I give you a task to print the elements of tuple, the first thing that comes to your mind is: < ... Args> { ( &elem : t) << elem << ; } template typename void print ( ::tuple<Args...> &t) const std for const auto // Error: no begin/end iterator cout endl But, this just can’t work. doesn’t have begin & end iterator. std::tuple OK! So, now you might try raw loop right? < ... Args> { ( i = ; i < ...(Args); ++i) << ::get<i>(t) << ; } template typename void print ( ::tuple<Args...>& t) const std for int 0 sizeof cout std endl // Error :( , `i` needs to be compile time constant No! you can’t. I know that std::get<> works with a number as . non-type template argument But, that number has to be compile-time constant to make this working. So, there are many solutions & we will go through quite enough ones. C++11: Loop Through Tuple Elements < i, ... Args> { << get<i>(t) << ; printer<i + , Args...>::print(t); } }; < ... Args> {} }; < ... Args> { printer< , Args...>::print(t); } tuple< , , > t( , , ); print(t); // Template recursion template size_t typename { struct printer static void print ( tuple<Args...> &t) const cout endl 1 // Terminating template specialisation template typename <sizeof...(Args), Args...> { struct printer static void print ( tuple<Args...> &) const template typename void print ( tuple<Args...> &t) const 0 int char string 1 'A' "ABC" // Note: might not work in GCC, I've used clang This isn’t that complicated as it looks, believe me. If you know recursion & template specialisation, it won’t take you more than 30 seconds to figure out what’s going on here. For our example tuple<int, char, string> t(1, 'A', "ABC");, printer::print() calls template recursion i,e, template<size_t i, typename… Args> struct printer{}; each time with incremented non-type template parameter i. And when i == sizeof…(Args), our recusion stops by calling template specialization i.e. template<typename… Args> struct printer<sizeof…(Args), Args…> { };. C++17: Loop Through Tuple Elements With C++ 17, it’s slightly better because we have Fold Expressions. So, we don’t need recursion anymore. < ... Args> { ::apply([]( &... args) { (( << args << ), ...); }, t); } template typename void print ( ::tuple<Args...> &t) const std std const auto cout endl designed as tuple helper that accepts functor or . Though you can do better if wants to dispatch to different implementation according to type, you might use overloaded class as: std::apply lambda expression < Ts... { Ts:: ()...; }; < f = overloaded { []( &a) { << << a << ; }, []( &b) { << << b << ; }, []( &c) { << << c << ; }, }; tuple< , , > t( , , ); ::apply([&]( &... e) { (f(e), ...); }, t); template ... > : class Ts struct overloaded using operator // Deduction guide, google `CTAD for aggregates` for more info template ... > ( ...) -> <Ts...>; class Ts overloaded Ts overloaded // not needed from C++20 auto const int cout "From int: " endl const char cout "From char: " endl const string cout "From string: " endl int char string 1 'A' "ABC" std const auto C++23: Loop Through Tuple Elements < ... Args> { ... ( &elem : t) << elem << ; } template typename void print ( ::tuple<Args...> &t) const std for const auto cout endl So, from C++23 we might have i.e. for...(). That looks like a loop, though it isn’t. It just stencil out each call with scope as: expansion statement < ... Args> { { &elem = get< >(t); << elem << ; } { &elem = get< >(t); << elem << ; } { &elem = get< >(t); << elem << ; } } template typename void print ( tuple<Args...> &t) const const auto 0 cout endl const auto 1 cout endl const auto 2 cout endl And it is obvious that there is no break & continue as it isn’t loop. It basically works for every standard container which can access by std::get<>(). For example, a plain array, std::tuple, std::pair, std::array, unexpanded argument packs, constexpr ranges, etc. Closing Words There are still many things missing in our tuple class like , , some operators and helper classes(like ). But I hope now you get the idea of how it can be implemented using the variadic template. By the way, implementing those missing things will be a good start for learning variadic template on your own. copy constructor move constructors std::tuple_size Previously published at http://www.vishalchovatiya.com/variadic-template-cpp-implementing-unsophisticated-tuple/