2011-03-30 15 views
22

¿Cómo se dividen los argumentos de plantilla variadic en dos mitades? Algo así como:dividir argumentos de plantilla variad

template <int d> struct a { 
    std::array <int, d> p, q; 
    template <typename ... T> a (T ... t) : p ({half of t...}), q ({other half of t...}) {} 
}; 
+0

pregunta interesante. Pensé que, dado que probablemente puedas dividir los args en tuplas, se podría comenzar con esto: http://ideone.com/YyeNC. Lamentablemente, los paquetes de parámetros múltiples para el constructor son ilegales, a pesar de que en esta situación debe quedar claro cuáles son los tipos en cada paquete. – visitor

Respuesta

4

todavía carecemos de una gran cantidad de ayudantes para manipular paquetes de parámetros variadic (o yo no soy consciente de ellos). Hasta que una buena biblioteca de Boost nos las traiga, aún podemos escribir la nuestra.

Por ejemplo, si usted está dispuesto a posponer su matrices de inicialización al cuerpo del constructor, puede crear y utilizar un fonction que copia parte del paquete de parámetro para un iterador de salida:

#include <array> 
#include <cassert> 
#include <iostream> 

// 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 

Como se puede vea, puede (no tan) crear fácilmente sus propios algoritmos para manipular paquetes de parámetros; todo lo que se necesita es una buena comprensión de la recurrencia y la coincidencia de patrones (como siempre al hacer MetaProgramación de plantillas ...).

+0

Tal biblioteca de impulso ya existe: boost.fusion. Si uno convierte el paquete de argumentos en un 'tuple', boost.fusion podría usarse trivialmente para dividir el' tuple' en dos 'tuple's o' boost :: fusion :: vector's separados. Codificaría una demostración, pero solo tengo VC++ a mano en este momento, que carece de argumentos de plantilla variados. – ildjarn

+0

Soy consciente de la biblioteca Boost.Fusion, pero yo estaba pensando en una biblioteca que se ocuparía de la naturaleza recursiva de paquetes de argumentos: debemos ser capaces de manipular el paquete argumento sin convertirlo a una tupla en primer lugar, esto implica el cálculo innecesario . Sin embargo, es cierto que el código que di podría simplificarse al usar Fusion (quizás publique otra respuesta para mostrar cómo se puede hacer). –

10

La solución de Luc es limpia y directa, pero le falta mucha diversión.
Debido a que sólo hay una forma correcta de utilizar plantillas variadic y es abusar de ellos para hacer cosas locas metaprogramming excesivamente complicada :)

De esta manera:

template <class T, size_t... Indx, class... Ts> 
std::array<T, sizeof...(Indx)> 
split_array_range_imp(pack_indices<Indx...> pi, Ts... ts) 
{ 
    return std::array<T, sizeof...(Indx)>{get<Indx>(ts...)...}; //TADA 
} 


template <class T, size_t begin, size_t end, class... Ts> 
std::array<T, end - begin> 
split_array_range(Ts... ts) 
{ 
    typename make_pack_indices<end, begin>::type indices; 
    return split_array_range_imp<T>(indices, ts...); 
} 

template <size_t N> 
struct DoubleArray 
{ 
    std::array <int, N> p, q; 

    template <typename ... Ts> 
    DoubleArray (Ts ... ts) : 
    p(split_array_range<int, 0    , sizeof...(Ts)/2 >(ts...)), 
    q(split_array_range<int, sizeof...(Ts)/2, sizeof...(Ts)  >(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; 
} 

Es bastante corto, excepto que necesitamos para codificar alguna ayuda:

Primero necesitamos la estructura make_pack_indices, que se usa para generar un rango de entero en tiempo de compilación. Por ejemplo make_pack_indices<5, 0>::type es en realidad el tipo pack_indices<0, 1, 2, 3, 4>

template <size_t...> 
struct pack_indices {}; 

template <size_t Sp, class IntPack, size_t Ep> 
struct make_indices_imp; 

template <size_t Sp, size_t ... Indices, size_t Ep> 
struct make_indices_imp<Sp, pack_indices<Indices...>, Ep> 
{ 
    typedef typename make_indices_imp<Sp+1, pack_indices<Indices..., Sp>, Ep>::type type; 
}; 

template <size_t Ep, size_t ... Indices> 
struct make_indices_imp<Ep, pack_indices<Indices...>, Ep> 
{ 
    typedef pack_indices<Indices...> type; 
}; 

template <size_t Ep, size_t Sp = 0> 
struct make_pack_indices 
{ 
    static_assert(Sp <= Ep, "__make_tuple_indices input error"); 
    typedef typename make_indices_imp<Sp, pack_indices<>, Ep>::type type; 
}; 

También necesitamos una función get(), muy similar a std :: conseguir para tupla, como std::get<N>(ts...) retorno del elemento enésimo de un paquete de parámetros.

template <class R, size_t Ip, size_t Ij, class... Tp> 
struct Get_impl 
{ 
    static R& dispatch(Tp...); 
}; 

template<class R, size_t Ip, size_t Jp, class Head, class... Tp> 
struct Get_impl<R, Ip, Jp, Head, Tp...> 
{ 
    static R& dispatch(Head& h, Tp&... tps) 
    { 
     return Get_impl<R, Ip, Jp + 1, Tp...>::dispatch(tps...); 
    } 
}; 

template<size_t Ip, class Head, class... Tp> 
struct Get_impl<Head, Ip, Ip, Head, Tp...> 
{ 
    static Head& dispatch(Head& h, Tp&... tps) 
    { 
     return h; 
    } 
}; 


template <size_t Ip, class ... Tp> 
typename pack_element<Ip, Tp...>::type& 
get(Tp&... tps) 
{ 
    return Get_impl<typename pack_element<Ip, Tp...>::type, Ip, 0, Tp...>::dispatch(tps...); 
} 

Pero para construir get() también necesitamos una estructura pack_element ayudante, de nuevo, muy similar a std :: tuple_element, como pack_element<N, Ts...>::type es del tipo N de la manada parámetros.

template <size_t _Ip, class _Tp> 
class pack_element_imp; 

template <class ..._Tp> 
struct pack_types {}; 

template <size_t Ip> 
class pack_element_imp<Ip, pack_types<> > 
{ 
public: 
    static_assert(Ip == 0, "tuple_element index out of range"); 
    static_assert(Ip != 0, "tuple_element index out of range"); 
}; 

template <class Hp, class ...Tp> 
class pack_element_imp<0, pack_types<Hp, Tp...> > 
{ 
public: 
    typedef Hp type; 
}; 

template <size_t Ip, class Hp, class ...Tp> 
class pack_element_imp<Ip, pack_types<Hp, Tp...> > 
{ 
public: 
    typedef typename pack_element_imp<Ip-1, pack_types<Tp...> >::type type; 
}; 

template <size_t Ip, class ...Tp> 
class pack_element 
{ 
public: 
    typedef typename pack_element_imp<Ip, pack_types<Tp...> >::type type; 
}; 

Y aquí vamos.
En realidad, realmente no entiendo por qué pack_element y get() ya no están en la biblioteca estándar. Esos ayudantes están presentes para std :: tuple, ¿por qué no para el paquete de parámetros?

Nota: Mi implementación de pack_element y make_pack_indices es una transposición directa de std :: tuple_element y __make_tuple_indices implementación encontrada en libC++.

+0

Creo que esta es la solución más limpia. Cuanto más lo miro, más me gusta. – Thomas

+0

IMO esta debería ser la respuesta aceptada porque esta es la forma correcta de hacerlo. Copiar elementos durante el tiempo de ejecución tiene el mismo efecto, pero viene con una penalización de rendimiento que es completamente evitable. –

+0

¿No es mejor utilizar la referencia constante en lugar de la referencia? ¿En qué casos se preferiría usar la referencia? Compilé todo usando una referencia constante y funcionó bien. – prestokeys

0

Aquí es otra solución:

#include <array> 
#include <tuple> 
#include <iostream> 

template <int i, int o> struct cpyarr_ { 
    template < typename T, typename L > static void f (T const& t, L &l) { 
    l[i-1] = std::get<i-1+o> (t); 
    cpyarr_<i-1,o>::f (t,l); 
    } 
}; 

template <int o> struct cpyarr_ <0,o> { 
    template < typename T, typename L > static void f (T const&, L&) {} 
}; 

template <int i, int o, typename U, typename ... T> std::array < U, i > cpyarr (U u, T... t) { 
    std::tuple < U, T... > l { u, t... }; 
    std::array < U, i > a; 
    cpyarr_<i,o>::f (l, a); // because std::copy uses call to memmov which is not optimized away (at least with g++ 4.6) 
    return a; 
} 

template <int d> struct a { 
    std::array <int, d> p, q; 
    template <typename ... T> a (T ... t) : p (cpyarr<d,0> (t...)), q (cpyarr<d,d> (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 << x.p[i] << " " << x.q[i] << "\n"; 
} 
0

Sé que esta pregunta es bastante antiguo, pero lo encontré ayer en la búsqueda de una solución a un problema muy similar. Resolví una solución y terminé escribiendo una pequeña biblioteca que creo que hace lo que quiere. Puede encontrar una descripción here si todavía está interesado.

2

Tenga en cuenta que en este caso particular, es posible utilizar std::initializer_list:

template<int... Is> struct index_sequence{}; 

template<int N, int... Is> struct make_index_sequence 
{ 
    typedef typename make_index_sequence<N - 1, N - 1, Is...>::type type; 
}; 

template<int... Is> struct make_index_sequence<0, Is...> 
{ 
    typedef index_sequence<Is...> type; 
}; 

template <int d> struct a { 
    std::array <int, d> p, q; 

    constexpr a (const std::initializer_list<int>& t) : 
     a(t, typename make_index_sequence<d>::type()) 
    {} 

private: 
    template <int... Is> 
    constexpr a(const std::initializer_list<int>& t, index_sequence<Is...>) : 
     p ({{(*(t.begin() + Is))...}}), 
     q ({{(*(t.begin() + d + Is))...}}) 
    {} 
}; 
Cuestiones relacionadas