Scinder les arguments de modèles variadiques

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(ts...) 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::type 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é.