First explain how it works:
This is a forward declaration so we can use flatten with one type.
template<class T> struct flatten;
Then we specialize flatten to accept any type of template. C is a template template parameter, so we can get the type of the surrounding template. For example. if you use flatten with the parameter std::tuple<int, double> , then C will be std::tuple .
FArgs is used to retrieve the parameter list of the passed pattern. In the case of the example I just talked about, that would be int, double
template< template< typename ... > class C, typename ...FArgs> struct flatten<C<FArgs...>>
In the rest of the flatten implementation, the C shell type and the FArgs argument list are now always available.
Forward the declaration for append , which adds the B parameter to the list of elements in the Target parameter
template< typename Target, typename B > struct append;
An append specialization that retrieves a list of types from Target (see above). Note that C is still std::tuple .
So, we add another element to the list and create a new type using the previous Args1 argument list and then adding T
template< typename ...Args1, typename T > struct append<C<Args1...>, T> { using type = C<Args1..., T>; };
Forward declaration inner and inner2
inner used to iterate over the list of provided arguments and applies inner2 to each of them, which in turn applies inner to the type again if the type is a different template with parameters. This pattern must match pattern C
template< typename Target, typename ...Args > struct inner; template< typename Target, typename T > struct inner2;
This inner2 processes all types of C template types and iterates through its parameter list recursively using inner
template< typename Target, typename ...Args> struct inner2<Target, C<Args...>> { using type = typename inner<Target, Args...>::type; };
If this specialization inner2 used, the type will be added to the final result.
template< typename Target, typename T > struct inner2 { using type = typename append<Target, T>::type; };
Returns template parameters by inheritance and application for each parameter inner2
template< typename Target, typename T, typename ...Args> struct inner<Target, T, Args...> : inner<typename inner2<Target, T>::type, Args...> {};
The final condition for recursion.
template< typename Target, typename T > struct inner<Target, T> { using type = typename inner2<Target, T>::type; };
This whole thing is generated from above, C<> indicates that Target and FArgs are arguments highlighted by the flatten specialization.
using type = typename inner<C<>, FArgs...>::type; };
And here is the whole code:
#include <tuple> #include <string> template<class T> struct flatten; template< template< typename ... > class C, typename ...FArgs> struct flatten<C<FArgs...>> { template< typename Target, typename ...B > struct append; template< typename ...Args1, typename T > struct append<C<Args1...>, T> { using type = C<Args1..., T>; }; template< typename Target, typename ...Args > struct inner; template< typename Target, typename T > struct inner2; template< typename Target, typename ...Args> struct inner2<Target, C<Args...>> { using type = typename inner<Target, Args...>::type; }; template< typename Target, typename T > struct inner2 { using type = typename append<Target, T>::type; }; template< typename Target, typename T, typename ...Args> struct inner<Target, T, Args...> : inner<typename inner2<Target, T>::type, Args...> {}; template< typename Target, typename T > struct inner<Target, T> { using type = typename inner2<Target, T>::type; }; using type = typename inner<C<>, FArgs...>::type; }; int main() { typedef flatten<std::tuple<int, float, double>>::type first; static_assert(std::is_same<first, std::tuple<int, float, double>>::value, "First not the same"); typedef flatten<std::tuple<int, std::tuple<float, double>>>::type second; static_assert(std::is_same<second, std::tuple<int, float, double>>::value, "Second not the same"); typedef flatten<std::tuple<int, std::tuple<char const *>, std::tuple<std::tuple<float, int>, double>>>::type third; static_assert(std::is_same<third, std::tuple<int, char const *, float, int, double>>::value, "Third not the same"); typedef flatten<std::tuple<int, std::tuple<std::tuple<std::tuple<std::tuple<char const *>>>>, std::tuple<std::tuple<float, int>, double>>>::type fourth; static_assert(std::is_same<fourth, std::tuple<int, char const *, float, int, double>>::value, "Fourth not the same"); typedef flatten<std::tuple<int, std::tuple<std::tuple<std::tuple<std::tuple<std::string>>>>, std::tuple<std::tuple<float, int>, double>>>::type fifth; static_assert(std::is_same<fifth, std::tuple<int, std::string, float, int, double>>::value, "Fifth not the same"); }
Edit: I rewrote the implementation to make it more readable and shorter (inspired by @DyP) Edit2: explained the code