2012-03-22 11 views
33

Función one() acepta un paquete de parámetros. La función two() acepta dos. Cada paquete está restringido para ser envuelto en los tipos A y B. ¿Por qué es imposible crear una instancia de two()?¿Cómo puedo tener múltiples paquetes de parámetros en una plantilla variadic?

template <typename T> 
struct A {}; 

template <typename T> 
struct B {}; 

template <typename... Ts> 
void one(A<Ts> ...as) { 
} 

template <typename... Ts, typename... Us> 
void two(A<Ts> ...as, B<Us> ...bs) { 
} 

int main() { 
    auto a = A<int>(); 
    auto b = B<int>(); 

    // Just fine 
    one(); 
    one(a); 
    one(a, a); 

    // All errors  
    two(); 
    two(a); 
    two(a, b); 
} 

Probado con gcc y clang.

[email protected]:~/x/cpp$ gcc -std=c++0x variadic_templates.cpp 
variadic_templates.cpp: In function ‘int main()’: 
variadic_templates.cpp:23:7: error: no matching function for call to ‘two()’ 
variadic_templates.cpp:23:7: note: candidate is: 
variadic_templates.cpp:11:6: note: template<class ... Ts, class ... Us> void two(A<Ts>..., B<Us>...) 
variadic_templates.cpp:24:8: error: no matching function for call to ‘two(A<int>&)’ 
variadic_templates.cpp:24:8: note: candidate is: 
variadic_templates.cpp:11:6: note: template<class ... Ts, class ... Us> void two(A<Ts>..., B<Us>...) 
variadic_templates.cpp:25:11: error: no matching function for call to ‘two(A<int>&, B<int>&)’ 
variadic_templates.cpp:25:11: note: candidate is: 
variadic_templates.cpp:11:6: note: template<class ... Ts, class ... Us> void two(A<Ts>..., B<Us>...) 
[email protected]:~/x/cpp$ clang -std=c++0x variadic_templates.cpp 
variadic_templates.cpp:23:3: error: no matching function for call to 'two' 
    two(); 
    ^~~ 
variadic_templates.cpp:11:6: note: candidate function template not viable: requires at least 1 argument, but 0 were provided                             
void two(A<Ts> ...as, B<Us> ...bs) {} 
    ^
variadic_templates.cpp:24:3: error: no matching function for call to 'two'                                         
    two(a); 
    ^~~ 
variadic_templates.cpp:11:6: note: candidate function not viable: requires 0 arguments, but 1 was provided                                 
void two(A<Ts> ...as, B<Us> ...bs) {} 
    ^
variadic_templates.cpp:25:3: error: no matching function for call to 'two'                                         
    two(a, b); 
    ^~~ 
variadic_templates.cpp:11:6: note: candidate function not viable: requires 0 arguments, but 2 were provided                                 
void two(A<Ts> ...as, B<Us> ...bs) {} 
    ^
3 errors generated. 
+0

'Un ... as' - ¿Es esto legal como un parámetro? – Xeo

+2

¿Cómo sabría el compilador cuándo termina un paquete y comienza el otro? – ildjarn

+0

@Xeo: es legal AFAIK. Dos compiladores lo aceptan. –

Respuesta

11

Encontré una solución. Envuelva cada paquete de parámetros en un Tuple. Usa una estructura para especialización parcial. Aquí hay una demostración que reenvía argumentos a un funtor al consumir una tupla como una lista y acumular otra. Bueno, este avanza copiando. Las tuplas se utilizan en la deducción de tipos, pero no se utilizan tuplas en los parámetros de las funciones, lo que creo que es perfecto.

#include <iostream> 
#include <tuple> 

template < typename ... > 
struct two_impl {}; 

// Base case 
template < typename F, 
      typename ...Bs > 
struct two_impl < F, std::tuple <>, std::tuple<Bs...> > { 
    void operator()(F f, Bs... bs) { 
    f(bs...); 
    } 
}; 

// Recursive case 
template < typename F, 
      typename A, 
      typename ...As, 
      typename ...Bs > 
struct two_impl < F, std::tuple< A, As... >, std::tuple< Bs...> > { 
    void operator()(F f, A a, As... as, Bs... bs) { 
    auto impl = two_impl < F, std::tuple <As...>, std::tuple < Bs..., A> >(); 
    impl(f, as..., bs..., a); 
    } 
}; 

template < typename F, typename ...Ts > 
void two(F f, Ts ...ts) { 
    auto impl = two_impl< F, std::tuple <Ts...>, std::tuple <> >(); 
    impl(f, ts...); 
} 

struct Test { 
    void operator()(int i, float f, double d) { 
    std::cout << i << std::endl << f << std::endl << d << std::endl; 
    } 
}; 

int main() { 
    two(Test(), 1, 1.5f, 2.1); 
} 

Tuplas son una muy buena lista de tiempo de compilación.

27

Aquí es otra manera de tener varios paquetes de parámetros utilizando los parámetros de plantilla Plantilla:

#include <iostream> 

template <typename... Types> 
struct foo {}; 

template < typename... Types1, template <typename...> class T 
     , typename... Types2, template <typename...> class V 
     , typename U > 
void 
bar(const T<Types1...>&, const V<Types2...>&, const U& u) 
{ 
    std::cout << sizeof...(Types1) << std::endl; 
    std::cout << sizeof...(Types2) << std::endl; 
    std::cout << u << std::endl; 
} 

int 
main() 
{ 
    foo<char, int, float> f1; 
    foo<char, int> f2; 
    bar(f1, f2, 9); 
    return 0; 
} 
+0

¿Se puede aplicar este enfoque a las plantillas de clase también? –

+0

@DrewNoakes No lo creo, ya que utilizo los parámetros de las funciones para empaquetar los argumentos de la plantilla. –

+0

Tal vez usando 'decltype' de alguna manera ... –

Cuestiones relacionadas