Comment diviser les arguments de modèles variadiques en deux moitiés? Quelque chose comme:
template struct a { std::array p, q; template a (T ... t) : p ({half of t...}), q ({other half of t...}) {} };
Il nous manque encore beaucoup d’aides pour manipuler les packs de parameters variadiques (ou je ne les connais pas). Jusqu’à ce qu’une belle bibliothèque Boost nous les apporte, nous pouvons toujours écrire les nôtres.
Par exemple, si vous souhaitez reporter l’initialisation de vos tableaux au corps du constructeur, vous pouvez créer et utiliser une fonction qui copie une partie du pack de parameters dans un iterator de sortie:
#include #include #include // Copy n values from the parameter pack to an output iterator template < typename OutputIterator > void copy_n( size_t n, OutputIterator ) { assert ( n == 0 ); } template < typename OutputIterator, typename T, typename... Args > void copy_n( size_t n, OutputIterator out, const T & value, Args... args ) { if ( n > 0 ) { *out = value; copy_n( n - 1, ++out, args... ); } } // Copy n values from the parameter pack to an output iterator, starting at // the "beginth" element template < typename OutputIterator > void copy_range( size_t begin, size_t size, OutputIterator out ) { assert( size == 0 ); } template < typename OutputIterator, typename T, typename... Args > void copy_range( size_t begin, size_t size, OutputIterator out, T value, Args... args ) { if ( begin == 0 ) { copy_n( size, out, value, args... ); } else { copy_range( begin - 1, size, out, args... ); } } template < int N > struct DoubleArray { std::array< int, N > p; std::array< int, N > q; template < typename... Args > DoubleArray ( Args... args ) { copy_range( 0, N, p.begin(), args... ); copy_range( N, N, q.begin(), args... ); } }; int main() { DoubleArray<3> mya(1, 2, 3, 4, 5, 6); std::cout << mya.p[0] << mya.p[2] << std::endl; std::cout << mya.q[0] << mya.q[2] << std::endl; } // Ouput: // 13 // 46
Comme vous pouvez le constater, vous pouvez (pas si) créer facilement vos propres algorithmes pour manipuler les packs de parameters; tout ce dont vous avez besoin est d’une bonne compréhension de la récursion et de la correspondance de motifs (comme toujours lorsque vous effectuez une métaprogrammation de modèle, etc.).
La solution de Luc est claire et directe, mais manque cruellement d’amusement.
Parce qu’il n’ya qu’une façon d’utiliser les modèles variadiques, c’est d’en abuser pour faire des métaprogrammations folles et compliquées 🙂
Comme ça :
template std::array split_array_range_imp(pack_indices pi, Ts... ts) { return std::array{get(ts...)...}; //TADA } template std::array split_array_range(Ts... ts) { typename make_pack_indices::type indices; return split_array_range_imp(indices, ts...); } template struct DoubleArray { std::array p, q; template DoubleArray (Ts ... ts) : p( split_array_range(ts...) ), q( split_array_range(ts...) ) { } }; int main() { DoubleArray<3> mya{1, 2, 3, 4, 5, 6}; std::cout << mya.p[0] << "\n" << mya.p[1] << "\n" << mya.p[2] << std::endl; std::cout << mya.q[0] << "\n" << mya.q[1] << "\n" << mya.q[2] << std::endl; }
C'est assez court, sauf qu'il faut coder une aide:
Nous avons d’abord besoin de la structure make_pack_indices, qui permet de générer une plage d’entiers au moment de la compilation. Par exemple, make_pack_indices<5, 0>::type
est en fait le type pack_indices<0, 1, 2, 3, 4>
template struct pack_indices {}; template struct make_indices_imp; template struct make_indices_imp, Ep> { typedef typename make_indices_imp, Ep>::type type; }; template struct make_indices_imp, Ep> { typedef pack_indices type; }; template struct make_pack_indices { static_assert(Sp <= Ep, "__make_tuple_indices input error"); typedef typename make_indices_imp, Ep>::type type; };
Nous avons également besoin d'une fonction get (), très similaire à std :: get pour tuple, telle que std::get
retourne le Nième élément d'un pack de parameters.
template struct Get_impl { static R& dispatch(Tp...); }; template struct Get_impl { static R& dispatch(Head& h, Tp&... tps) { return Get_impl::dispatch(tps...); } }; template struct Get_impl { static Head& dispatch(Head& h, Tp&... tps) { return h; } }; template typename pack_element::type& get(Tp&... tps) { return Get_impl::type, Ip, 0, Tp...>::dispatch(tps...); }
Mais pour construire get (), nous avons également besoin d’une structure d’assistance pack_element, très similaire à std :: tuple_element, telle que pack_element
est le nième type du pack de parameters.
template class pack_element_imp; template struct pack_types {}; template class pack_element_imp > { public: static_assert(Ip == 0, "tuple_element index out of range"); static_assert(Ip != 0, "tuple_element index out of range"); }; template class pack_element_imp<0, pack_types > { public: typedef Hp type; }; template class pack_element_imp > { public: typedef typename pack_element_imp >::type type; }; template class pack_element { public: typedef typename pack_element_imp >::type type; };
Et c'est reparti.
En fait, je ne comprends pas vraiment pourquoi pack_element et get () ne sont pas déjà dans la bibliothèque standard. Ces aides sont présents pour std :: tuple, pourquoi pas pour le pack de parameters?
Remarque: Mon implémentation de pack_element et make_pack_indices est une transposition directe de l'implémentation std :: tuple_element et __make_tuple_indices trouvée dans libc ++.
Notez que dans ce cas particulier, vous pouvez utiliser std::initializer_list
:
template struct index_sequence{}; template struct make_index_sequence { typedef typename make_index_sequence::type type; }; template struct make_index_sequence<0, Is...> { typedef index_sequence type; }; template struct a { std::array p, q; constexpr a (const std::initializer_list & t) : a(t, typename make_index_sequence::type()) {} private: template constexpr a(const std::initializer_list& t, index_sequence) : p ({{(*(t.begin() + Is))...}}), q ({{(*(t.begin() + d + Is))...}}) {} };
Voici encore une autre solution:
#include #include #include template struct cpyarr_ { template < typename T, typename L > static void f (T const& t, L &l) { l[i-1] = std::get (t); cpyarr_::f (t,l); } }; template struct cpyarr_ <0,o> { template < typename T, typename L > static void f (T const&, L&) {} }; template std::array < U, i > cpyarr (U u, T... t) { std::tuple < U, T... > l { u, t... }; std::array < U, i > a; cpyarr_::f (l, a); // because std::copy uses call to memmov which is not optimized away (at least with g++ 4.6) return a; } template struct a { std::array p, q; template a (T ... t) : p (cpyarr (t...)), q (cpyarr (t...)) {} }; int main () { a <5> x { 0,1,2,3,4,5,6,7,8,9 }; for (int i = 0; i < 5; i++) std::cout << xp[i] << " " << xq[i] << "\n"; }
Je sais que cette question est assez ancienne, mais je l’ai trouvée seulement hier en cherchant une solution à un problème très similaire. J’ai moi-même trouvé une solution et fini par écrire une petite bibliothèque qui, je crois, fait ce que vous voulez. Vous pouvez trouver une description ici si vous êtes toujours intéressé.