I took a look at the std tuple header file (which I believe is authored/maintained by Stephan T. Lavavej of the VC++ team – original code by P.J. Plauger) and it would be an understatement to say that my head was spinning for a while just going through the code. To understand better how it was implemented, I simplified it down and extracted out the minimal code required to instantiate a tuple and to access its values (and be able to both read and write). It helped me understand how variadic templates are typically used with recursive expansion to significantly reduce lines of code when designing template classes.

// tuple template<class... _Types> class tuple; // empty tuple template<> class tuple<> {}; // recursive tuple definition template<class _This, class... _Rest> class tuple<_This, _Rest...> : private tuple<_Rest...> { public: _This _Myfirst; };

The recursive specialization uses inheritance so that we’ll end up with members for every argument type specified for the tuple. To access a tuple value, a tuple_element class is used as a sort of accessor class.

// tuple_element template<size_t _Index, class _Tuple> struct tuple_element; // select first element template<class _This, class... _Rest> struct tuple_element<0, tuple<_This, _Rest...>> { typedef _This& type; typedef tuple<_This, _Rest...> _Ttype; }; // recursive tuple_element definition template <size_t _Index, class _This, class... _Rest> struct tuple_element<_Index, tuple<_This, _Rest...>> : public tuple_element<_Index - 1, tuple<_Rest...> > { };

Again, recursive inheritance is used, and the 0th case is specialized as well. Notice the two typedefs, one of them is a reference to the type of the value, and the other represents the tuple with the same arguments as the tuple_element. So, given an _Index value, we can retrieve the type of the tuple and the type of the tuple value for that recursion level. This is used in the get method.

// get reference to _Index element of tuple template<size_t _Index, class... _Types> inline typename tuple_element<_Index, tuple<_Types...>>::type get(tuple<_Types...>& _Tuple) { typedef typename tuple_element<_Index, tuple<_Types...>>::_Ttype _Ttype; return (((_Ttype&) _Tuple)._Myfirst); }

Notice the return type, it uses the type typedef defined above. Similarly, the tuple is cast to the _TType typedef defined above and then we access the _Myfirst member (which represents the value). Now you can write code as follows.

tuple<int, char> t1; get<0>(t1) = 959; get<1>(t1) = 'A'; auto v1 = get<0>(t1); auto v2 = get<1>(t1);

Now, this goes without saying, but I’ll say it just to be sure – but this is just for demonstration. Do not use this in production code, instead use std::tuple which does all this and lots more (there’s a reason it’s 800 lines long).

Advertisements