template metaprogramming :why flat type is failure
c++, c++11, templates
Solution
Here is my stab at this. I tried to document what is going on to make it clear:
We start with Flatten. It takes a type. We will specialize it below:
template<typename T>
struct Flatten;
Here is our workhorse. Takes Src, and flattens its contents and appends it to Dest:
template<typename Dest, typename Src>
struct Flatten_append;
An empty right hand side pack means return left hand side:
template<template<typename...>class Pack, typename... LHS>
struct Flatten_append< Pack<LHS...>, Pack<> > {
typedef Pack<LHS...> type;
};
A right hand side whose first argument is a Pack<...> should be flattened before processing:
template<template<typename...>class Pack, typename... LHS, typename... RHS0, typename... RHSrest>
struct Flatten_append< Pack<LHS...>, Pack<Pack<RHS0...>, RHSrest... > >:
Flatten_append< Pack<LHS...>, Pack< RHS0..., RHSrest... > >
{};
Otherwise, a non-empty right hand side pack should have its first element moved over to the left hand side: (this will match weaker than the above, as it is less specialized)
template<template<typename...>class Pack, typename... LHS, typename RHS0, typename... RHSrest>
struct Flatten_append< Pack<LHS...>, Pack<RHS0, RHSrest... > >:
Flatten_append< Pack<LHS..., RHS0>, Pack< RHSrest... > >
{};
Implement Flatten in terms of Flatten_append to an empty Pack:
template<template<typename...>class Pack, typename... Ts>
struct Flatten< Pack<Ts...> >:Flatten_append< Pack<>, Pack<Ts...> > {};
The goal was to make it as clear what is going on as possible.
Now, you'll note that a downside to this design is that it will flatten any `template` that only contains types. We probably want to pass in the pack that we want to flatten.
template<template<typename...>class Pack, typename T>
struct Flatten;
template<template<typename...>class Pack, typename Dest, typename Src>
struct Flatten_append;
template<template<typename...>class Pack, typename... Ts>
struct Flatten< Pack<Ts...> > : Flatten_append< Pack, Pack<>, Pack<Ts...> > {};
and then change each specialization of `Flatten_append< blah, blah, blah >` to `Flatten_append< Pack, blah, blah, blah >`.
This means you pass in the `template` you want to flatten explicitly, and the code only flattens that `template`.
In practice, this may not be needed, as the `Pack` type gets deduced from the left hand side type passed in.
Problem
I want to flatten a tree type to flat type. Example: ``` typedef std::tuple<int,std::tuple<int,long>,int> tup; Flat<tup>::type=>std::tuple<int,int,long,int> ``` I use: ``` template<typename T> struct Flat { using type=T; }; template <template <typename ...> class C,typename...ARGS> struct Flat<C<ARGS...> > { using type=C<ARGS...>; }; template <template <typename ...> class C,typename ...ARGS0,typename...ARGS1,typename ...ARGS2> struct Flat<C<ARGS0...,C<ARGS1...>,ARGS2...> > :Flat<C<ARGS0...,ARGS1...,ARGS2...> > { }; void test(){ typedef std::tuple<int,std::tuple<int,long>,int> tup; static_assert(std::is_same<typename Flat<tup>::type,std::tuple<int,int,long,int> >::value,""); } ``` but I get `std::tuple<int,std::tuple<int,long>,int>` still... I use gcc 4.8.1