Disons que j'ai
pack<int, int, int, int>
Je veux convertir
pack<int, pack<int, pack<int, pack<int>>>>
en
template<class ... T> pack { };
Comment puis-je le faire?
7 Réponses :
Une implémentation rapide possible basée sur std :: tuple_cat code>
:
template <class T> struct tuple_flatten { using type = std::tuple<T>; }; template <class... Args> struct tuple_flatten<pack<Args...>> { using type = decltype(std::tuple_cat( typename tuple_flatten<Args>::type{}...)); }; template <class T> struct tuple_to_pack; template <class... Args> struct tuple_to_pack<std::tuple<Args...>> { using type = pack<Args...>; }; template <class T> struct flatten { using type = typename tuple_to_pack< typename tuple_flatten<T>::type>::type; }; template <class T> using flatten_t = typename flatten<T>::type;
Je décompresserais et emballerais récursivement les choses:
template<class Head, class... Packed> struct repack { using type = Head; }; template<class Head, class... Packed> struct repack<pack<Head, pack<Packed...>>> { using type = pack<Head, repack<Packed...>>; };
Le type repack
se transforme en:
pack >>>>
pack >>>
pack >>
pack
Ce code ne génère pas le type que vous pensez qu'il génère. Il ne gère pas non plus `pack
Je propose la structure suivante et en utilisant
#include <tuple> #include <type_traits> template <typename...> struct pack { }; template <typename T0, typename...> struct flatt_helper { using type = T0; }; template <typename ... Ts1, typename T0, typename ... Ts2> struct flatt_helper<pack<Ts1...>, T0, Ts2...> : flatt_helper<pack<Ts1..., T0>, Ts2...> { }; template <typename ... Ts1, template <typename ...> class C, typename ... Ts2, typename ... Ts3> struct flatt_helper<pack<Ts1...>, C<Ts2...>, Ts3...> : flatt_helper<pack<Ts1...>, Ts2..., Ts3...> { }; template <typename T> using flatt = typename flatt_helper<pack<>, T>::type; int main() { using T0 = pack<int, pack<int, pack<int, pack<int>>>>; using T1 = pack<int, int, int, int>; using T2 = flatt<T0>; using T3 = pack<int, pack<int, long>, std::tuple<pack<int, char>, long>>; using T4 = pack<int, int, long, int, char, long>; using T5 = flatt<T3>; static_assert( std::is_same<T1, T2>::value, "!" ); static_assert( std::is_same<T4, T5>::value, "!" ); }
De cette façon, vous pouvez aplatir pack
et d'autres template-template, comme std :: tuple code>, et aussi des exemples plus complexes (le
pack
suggéré par Holt, par exemple).
Si vous ne voulez que plat pack
, en évitant que tous les template-template soient aplatis ..., je veux dire ... si vous voulez que
template <typename ... Ts1, typename ... Ts2, typename ... Ts3> struct flatt_helper<pack<Ts1...>, pack<Ts2...>, Ts3...> : flatt_helper<pack<Ts1...>, Ts2..., Ts3...> { };
soit aplati comme p>
pack<int, int, int, long>
au lieu de
pack<int, int, std::tuple<int, long>>
vous devez supprimer le paramètre template-template dans le dernier flatt_helper
spécialisation et simplifiez-la comme suit
pack<int, pack<int, std::tuple<int, long>>>
Ce qui suit est un exemple de compilation complet (avec aplatissement complet)
template <typename T0, typename...> struct flatt_helper { using type = T0; }; template <typename ... Ts1, typename T0, typename ... Ts2> struct flatt_helper<pack<Ts1...>, T0, Ts2...> : flatt_helper<pack<Ts1..., T0>, Ts2...> { }; template <typename ... Ts1, template <typename ...> class C, typename ... Ts2, typename ... Ts3> struct flatt_helper<pack<Ts1...>, C<Ts2...>, Ts3...> : flatt_helper<pack<Ts1...>, Ts2..., Ts3...> { }; template <typename T> using flatt = typename flatt_helper<pack<>, T>::type;
Bonne réponse avec la construction de pack<>
mais vous devez souligner que, avec le paramètre template template, cela aplatira tout, comme flatt
est en fait pack
, ce qui peut ne pas être le comportement attendu ici.
@Holt - oui, ce n'était pas clair; réponse modifiée; l'espoir est plus clair maintenant
pack_cat
prend une séquence de packs ou de non-packs, et concatine tout ce qui est un pack ensemble, plus les éléments qui ne sont pas des packs.
pack<int,int,int,int>{}=unpack<pack<int,pack<int,pack<int,pack<int>>>>>{}; pack<int,int,int>{} = unpack<pack<int,int,int>>{}; pack<int,int,int>{} = unpack<pack<pack<int,int>,int>>{};
unpacker
prend un pack, décompresse récursivement tous les sous-packs et les concatine.
template<class X> struct unpacker{using type=X;}; template<class X> using unpack=typename unpacker<X>::type; template<class...Ts> struct unpacker<pack<Ts...>>{using type=pack_cat<pack<>,unpack<Ts>...>;};
Pour le tester:
template<class T0, class...Ts> struct catter; template<class...Ts> using pack_cat = typename catter<Ts...>::type; template<class T0> struct catter<T0>{ using type=T0; }; template<class...Ts, class...Us, class...Vs> struct catter<pack<Ts...>, pack<Us...>, Vs...>{ using type=pack_cat<pack<Ts...,Us...>,Vs...>; }; template<class...Ts, class U, class...Vs> struct catter<pack<Ts...>, U, Vs...>{ using type=pack_cat<pack<Ts...,U>,Vs...>; };
qui compilent IFF les deux types sont les mêmes.
p>
template< typename > struct is_tuple: false_type{}; template<typename ... input_t> struct is_tuple< std::tuple<input_t ... > > : true_type{}; template<typename ... input_t> using flat_tuple= decltype(std::tuple_cat( std::declval< std::conditional_t< is_tuple< input_t >::value, input_t, std::tuple< input_t > > >()... ) );
Et maintenant ... pour quelque chose de complètement différent ... (enfin ... pas complètement ... presque les solutions de Holt mais en utilisant des fonctions au lieu de structs)
Vous pouvez combiner std :: tuple_cat ( )
avec decltype ()
dans un couple de fonction déclarée pour aplatir un pack
dans un std::tuple
#include <tuple> #include <type_traits> template <typename...> struct pack { }; template <typename T> constexpr std::tuple<T> foo (T); template <typename ... Ts> constexpr auto foo (pack<Ts...>) -> decltype( std::tuple_cat(foo(std::declval<Ts>())...) ); template <typename ... Ts> constexpr pack<Ts...> bar (std::tuple<Ts...>); template <typename T> using fl = decltype(bar(foo(std::declval<T>()))); int main() { using U0 = pack<int, pack<int, pack<int, pack<int>>>>; using U1 = pack<int, int, int, int>; using U2 = fl<U0>; using U3 = pack<int, pack<int, long>, pack<pack<int, char>, long>>; using U4 = pack<int, int, long, int, char, long>; using U5 = fl<U3>; static_assert( std::is_same<U1, U2>::value, "!" ); static_assert( std::is_same<U4, U5>::value, "!" ); }
et une autre fonction déclarée qui convertit à nouveau le std :: tuple
aplati dans un pack
template <typename T> using fl = decltype(bar(foo(std::declval<T>())));
En retard pour la fête?
template <class... Ts> struct flatten; template <class... Ts> struct flatten<pack<Ts...>, pack<>> { using type = pack<Ts...>; }; template <class... Ts> struct flatten<pack<Ts...>> : flatten<pack<>, pack<Ts...>> { }; template <class... Ts, class T, class... Rs> struct flatten<pack<Ts...>, pack<T, Rs...>> : flatten<pack<Ts...>, T, pack<Rs...>> { }; template <class... Ts, class T, class... Es> struct flatten<pack<Ts...>, T, pack<Es...>> : flatten<pack<Ts..., T>, pack<Es...>> { }; template <class... Ts, class... Rs, class... Es> struct flatten<pack<Ts...>, pack<Rs...>, pack<Es...>> : flatten<pack<Ts...>, pack<Rs..., Es...>> { }; template <class T> using flatten_t = typename flatten<T>::type; using T1 = pack<int, pack<int, int>, pack<int, int>, int>; using T2 = pack<int, pack<int, pack<int, pack<int, int>>>, int>; using R1 = pack<int,int,int,int,int,int>; static_assert(std::is_same_v<R1, flatten_t<T1>>); static_assert(std::is_same_v<R1, flatten_t<T2>>);
Voulez-vous également convertir des éléments tels que
pack, int>
?