¿Cómo puedo expandir una tupla en los argumentos de la función de plantilla variadic?

Considere el caso de una función de plantilla con argumentos de plantilla variadica:

template Tret func(const T&... t); 

Ahora, tengo una tupla de valores. ¿Cómo func() usando los valores de tupla como argumentos? He leído sobre el objeto de función bind() , con la función call() y también la función apply() en diferentes documentos ahora obsoletos. La implementación GNU GCC 4.4 parece tener una función call() en la clase bind() , pero hay muy poca documentación sobre el tema.

Algunas personas sugieren hacks recursivos escritos a mano, pero el verdadero valor de los argumentos de plantillas variadic es poder usarlos en casos como el anterior.

¿Alguien tiene una solución para el is, o insinúa dónde leer sobre él?

Aquí está mi código si alguien está interesado

Básicamente en tiempo de comstackción, el comstackdor desenrollará de forma recursiva todos los argumentos en varias llamadas a función inclusiva -> llamadas -> llamadas … -> llamadas <0> que es la última y el comstackdor optimizará las diversas llamadas a funciones intermedias solo conservan la última, que es el equivalente de func (arg1, arg2, arg3, …)

Se proporcionan 2 versiones, una para una función llamada en un objeto y otra para una función estática.

 #include  /** * Object Function Tuple Argument Unpacking * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @tparam N Number of tuple arguments to unroll * * @ingroup g_util_tuple */ template < uint N > struct apply_obj_func { template < typename T, typename... ArgsF, typename... ArgsT, typename... Args > static void applyTuple( T* pObj, void (T::*f)( ArgsF... ), const std::tr1::tuple& t, Args... args ) { apply_obj_func::applyTuple( pObj, f, t, std::tr1::get( t ), args... ); } }; //----------------------------------------------------------------------------- /** * Object Function Tuple Argument Unpacking End Point * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @ingroup g_util_tuple */ template <> struct apply_obj_func<0> { template < typename T, typename... ArgsF, typename... ArgsT, typename... Args > static void applyTuple( T* pObj, void (T::*f)( ArgsF... ), const std::tr1::tuple& /* t */, Args... args ) { (pObj->*f)( args... ); } }; //----------------------------------------------------------------------------- /** * Object Function Call Forwarding Using Tuple Pack Parameters */ // Actual apply function template < typename T, typename... ArgsF, typename... ArgsT > void applyTuple( T* pObj, void (T::*f)( ArgsF... ), std::tr1::tuple const& t ) { apply_obj_func::applyTuple( pObj, f, t ); } //----------------------------------------------------------------------------- /** * Static Function Tuple Argument Unpacking * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @tparam N Number of tuple arguments to unroll * * @ingroup g_util_tuple */ template < uint N > struct apply_func { template < typename... ArgsF, typename... ArgsT, typename... Args > static void applyTuple( void (*f)( ArgsF... ), const std::tr1::tuple& t, Args... args ) { apply_func::applyTuple( f, t, std::tr1::get( t ), args... ); } }; //----------------------------------------------------------------------------- /** * Static Function Tuple Argument Unpacking End Point * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @ingroup g_util_tuple */ template <> struct apply_func<0> { template < typename... ArgsF, typename... ArgsT, typename... Args > static void applyTuple( void (*f)( ArgsF... ), const std::tr1::tuple& /* t */, Args... args ) { f( args... ); } }; //----------------------------------------------------------------------------- /** * Static Function Call Forwarding Using Tuple Pack Parameters */ // Actual apply function template < typename... ArgsF, typename... ArgsT > void applyTuple( void (*f)(ArgsF...), std::tr1::tuple const& t ) { apply_func::applyTuple( f, t ); } // *************************************** // Usage // *************************************** template < typename T, typename... Args > class Message : public IMessage { typedef void (T::*F)( Args... args ); public: Message( const std::string& name, T& obj, F pFunc, Args... args ); private: virtual void doDispatch( ); T* pObj_; F pFunc_; std::tr1::tuple args_; }; //----------------------------------------------------------------------------- template < typename T, typename... Args > Message::Message( const std::string& name, T& obj, F pFunc, Args... args ) : IMessage( name ), pObj_( &obj ), pFunc_( pFunc ), args_( std::forward(args)... ) { } //----------------------------------------------------------------------------- template < typename T, typename... Args > void Message::doDispatch( ) { try { applyTuple( pObj_, pFunc_, args_ ); } catch ( std::exception& e ) { } } 

En C ++ hay muchas formas de expandir / desempaquetar tuplas y aplicar esos elementos de tupla a una función de plantilla variadica. Aquí hay una pequeña clase de ayuda que crea una matriz de índices. Se usa mucho en la metaprogtwigción de plantillas:

 // ------------- UTILITY--------------- template struct index_tuple{}; template struct make_indexes_impl; template struct make_indexes_impl, T, Types...> { typedef typename make_indexes_impl, Types...>::type type; }; template struct make_indexes_impl > { typedef index_tuple type; }; template struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...> {}; 

Ahora el código que hace el trabajo no es tan grande:

  // ----------UNPACK TUPLE AND APPLY TO FUNCTION --------- #include  #include  using namespace std; template Ret apply_helper( Ret (*pf)(Args...), index_tuple< Indexes... >, tuple&& tup) { return pf( forward( get(tup))... ); } template Ret apply(Ret (*pf)(Args...), const tuple& tup) { return apply_helper(pf, typename make_indexes::type(), tuple(tup)); } template Ret apply(Ret (*pf)(Args...), tuple&& tup) { return apply_helper(pf, typename make_indexes::type(), forward>(tup)); } 

La prueba se muestra a continuación:

 // --------------------- TEST ------------------ void one(int i, double d) { std::cout << "function one(" << i << ", " << d << ");\n"; } int two(int i) { std::cout << "function two(" << i << ");\n"; return i; } int main() { std::tuple tup(23, 4.5); apply(one, tup); int d = apply(two, std::make_tuple(2)); return 0; } 

No soy un gran experto en otros idiomas, pero supongo que si estos idiomas no tienen esa funcionalidad en su menú, no hay forma de hacerlo. Al menos con C ++ puedes, y creo que no es tan complicado …

Encuentro que esta es la solución más elegante (y está reenviada de manera óptima):

 #include  #include  #include  #include  template struct Apply { template static inline auto apply(F && f, T && t, A &&... a) -> decltype(Apply::apply( ::std::forward(f), ::std::forward(t), ::std::get(::std::forward(t)), ::std::forward(a)... )) { return Apply::apply(::std::forward(f), ::std::forward(t), ::std::get(::std::forward(t)), ::std::forward(a)... ); } }; template<> struct Apply<0> { template static inline auto apply(F && f, T &&, A &&... a) -> decltype(::std::forward(f)(::std::forward(a)...)) { return ::std::forward(f)(::std::forward(a)...); } }; template inline auto apply(F && f, T && t) -> decltype(Apply< ::std::tuple_size< typename ::std::decay::type >::value>::apply(::std::forward(f), ::std::forward(t))) { return Apply< ::std::tuple_size< typename ::std::decay::type >::value>::apply(::std::forward(f), ::std::forward(t)); } 

Ejemplo de uso:

 void foo(int i, bool b); std::tuple t = make_tuple(20, false); void m() { apply(&foo, t); } 

Desafortunadamente GCC (4.6 al menos) no puede comstackr esto con “lo siento, no implementado: sobrecarga de destrucción” (lo que simplemente significa que el comstackdor aún no implementa completamente la especificación C ++ 11), y dado que usa plantillas variadas, no lo hará trabajar en MSVC, por lo que es más o menos inútil. Sin embargo, una vez que haya un comstackdor que admita la especificación, será el mejor enfoque en mi humilde opinión. (Nota: no es tan difícil modificar esto para que pueda evitar las deficiencias en GCC, o implementarlo con Boost Preprocessor, pero arruina la elegancia, así que esta es la versión que estoy publicando).

GCC 4.7 ahora es compatible con este código muy bien.

Editar: añadido hacia adelante en función de la llamada a la función real para admitir la forma de referencia de valor * esto en caso de que esté utilizando clang (o si alguien más realmente consigue agregarlo).

Editar: se agregó la pérdida de avance hacia adelante alrededor del objeto de función en el cuerpo de la función aplicar no miembro. Gracias a pheedbaq por señalar que faltaba.

Editar: Y aquí está la versión C ++ 14 ya que es mucho más agradable (no se comstack todavía):

 #include  #include  #include  #include  template struct Apply { template static inline auto apply(F && f, T && t, A &&... a) { return Apply::apply(::std::forward(f), ::std::forward(t), ::std::get(::std::forward(t)), ::std::forward(a)... ); } }; template<> struct Apply<0> { template static inline auto apply(F && f, T &&, A &&... a) { return ::std::forward(f)(::std::forward(a)...); } }; template inline auto apply(F && f, T && t) { return Apply< ::std::tuple_size< ::std::decay_t >::value>::apply(::std::forward(f), ::std::forward(t)); } 

Aquí hay una versión para funciones de miembros (¡no probada mucho!):

 using std::forward; // You can change this if you like unreadable code or care hugely about namespace pollution. template struct ApplyMember { template static inline auto apply(C&& c, F&& f, T&& t, A&&... a) -> decltype(ApplyMember::apply(forward(c), forward(f), forward(t), std::get(forward(t)), forward(a)...)) { return ApplyMember::apply(forward(c), forward(f), forward(t), std::get(forward(t)), forward(a)...); } }; template<> struct ApplyMember<0> { template static inline auto apply(C&& c, F&& f, T&&, A&&... a) -> decltype((forward(c)->*forward(f))(forward(a)...)) { return (forward(c)->*forward(f))(forward(a)...); } }; // C is the class, F is the member function, T is the tuple. template inline auto apply(C&& c, F&& f, T&& t) -> decltype(ApplyMember::type>::value>::apply(forward(c), forward(f), forward(t))) { return ApplyMember::type>::value>::apply(forward(c), forward(f), forward(t)); } 
 // Example: class MyClass { public: void foo(int i, bool b); }; MyClass mc; std::tuple t = make_tuple(20, false); void m() { apply(&mc, &MyClass::foo, t); } 
 template auto apply_impl(F&& f, Tuple&& t, std::index_sequence) { return std::forward(f)(std::get(std::forward(t))...); } template auto apply(F&& f, Tuple&& t) { using Indices = std::make_index_sequence>::value>; return apply_impl(std::forward(f), std::forward(t), Indices()); } 

Esto se ha adaptado del borrador de C ++ 14 usando index_sequence. Podría proponerme aplicar en un futuro estándar (TS).

En C ++ 17 puedes hacer esto:

 std::apply(the_function, the_tuple); 

Esto ya funciona en Clang ++ 3.9, usando std :: experimental :: apply.

Respondiendo al comentario que dice que esto no funcionará si la the_function es the_function , lo siguiente es una the_function :

 #include  template  void my_func(T &&t, U &&u) {} int main(int argc, char *argv[argc]) { std::tuple my_tuple; std::apply([](auto &&... args) { my_func(args...); }, my_tuple); return 0; } 

Este trabajo alternativo es una solución simplificada al problema general de pasar los conjuntos de sobrecarga y la plantilla de función donde se esperaría una función. Aquí se presenta la solución general (una que se ocupa del reenvío perfecto, la consistencia y la no excepción): https://blog.tartanllama.xyz/passing-overload-sets/ .

Las noticias no se ven bien.

Después de leer el borrador del estándar recién publicado , no veo una solución incorporada a esto, que parece extraño.

El mejor lugar para preguntar acerca de tales cosas (si no lo has hecho) es comp.lang.c ++ moderado, porque algunas personas involucradas en el borrador de la publicación estándar allí regularmente.

Si revisa este hilo , alguien tiene la misma pregunta (¡tal vez sea usted, en cuyo caso encontrará frustrante toda esta respuesta!), Y se sugieren algunas implementaciones desagradables.

Me preguntaba si sería más fácil hacer que la función acepte una tuple , ya que la conversión de esa manera es más fácil. Pero esto implica que todas las funciones deben aceptar tuplas como argumentos, para una flexibilidad máxima, y ​​eso solo demuestra la rareza de no proporcionar una expansión incorporada de tupla para funcionar con el paquete de argumentos.

Actualización: el enlace de arriba no funciona, intente pegar esto:

http://groups.google.com/group/comp.lang.c++moderated/browse_thread/thread/750fa3815cdaac45/d8dc09e34bbb9661?lnk=gst&q=tuple+variadic#d8dc09e34bbb9661

Todas estas implementaciones son buenas. Pero debido al uso del puntero al comstackdor de la función miembro a menudo no se puede alinear la llamada a la función objective (al menos no puede gcc 4.8, no importa por qué gcc no puede punteros de función en línea que se pueden determinar? )

Pero las cosas cambian si se envía el puntero a la función de miembro como argumentos de plantilla, no como parámetros de función:

 /// from https://stackoverflow.com/a/9288547/1559666 template struct seq {}; template struct gens : gens {}; template struct gens<0, S...>{ typedef seq type; }; template using makeSeq = typename gens< std::tuple_size< typename std::decay::type >::value >::type; // deduce function return type template struct fn_type; template struct fn_type< std::tuple >{ // will not be called template static auto type_helper(Self &self, Fn f) -> decltype((self.*f)(declval()...)){ //return (self.*f)(Args()...); return NULL; } }; template struct APPLY_TUPLE{}; template struct APPLY_TUPLE>{ Self &self; APPLY_TUPLE(Self &self): self(self){} template void delayed_call(Tuple &&list){ caller(forward(list), makeSeq() ); } template void caller(Tuple &&list, const seq){ (self.*f)( std::get(forward(list))... ); } }; #define type_of(val) typename decay::type #define apply_tuple(obj, fname, tuple) \ APPLY_TUPLE::type, typename decay::type >(obj).delayed_call< \ decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay::type::fname) ), \ &decay::type::fname \ > \ (tuple); 

Y uso:

 struct DelayedCall { void call_me(int a, int b, int c){ std::cout << a+b+c; } void fire(){ tuple list = make_tuple(1,2,3); apply_tuple(*this, call_me, list); // even simpler than previous implementations } }; 

Prueba de http://goo.gl/5UqVnC inlinable


Con pequeños cambios, podemos “sobrecargar” apply_tuple :

 #define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N #define VA_NARGS(...) VA_NARGS_IMPL(X,##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0) #define VARARG_IMPL_(base, count, ...) base##count(__VA_ARGS__) #define VARARG_IMPL(base, count, ...) VARARG_IMPL_(base, count, __VA_ARGS__) #define VARARG(base, ...) VARARG_IMPL(base, VA_NARGS(__VA_ARGS__), __VA_ARGS__) #define apply_tuple2(fname, tuple) apply_tuple3(*this, fname, tuple) #define apply_tuple3(obj, fname, tuple) \ APPLY_TUPLE::type, typename decay::type >(obj).delayed_call< \ decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay::type::fname) ), \ &decay::type::fname \ /* ,decltype(tuple) */> \ (tuple); #define apply_tuple(...) VARARG(apply_tuple, __VA_ARGS__) ... apply_tuple(obj, call_me, list); apply_tuple(call_me, list); // call this->call_me(list....) 

Además, esta es la única solución que funciona con funciones de plantilla.

1) si tiene una estructura de paquete de parámetros preparada como argumento de función, puede usar std :: tie como este:

 template  void tie_func(std::tuple t, Args&... args) { std::tie(args...) = t; } int main() { std::tuple t(2, 3.3, "abc"); int i; double d; std::string s; tie_func(t, i, d, s); std::cout << i << " " << d << " " << s << std::endl; } 

2) si no tienes un arpa parampack lista para usar, tendrás que desenrollar la tupla como esta

 #include  #include  #include  template struct apply_wrap { template static R applyTuple( std::function& f, const std::tuple& t, UnpackedArgs... args ) { return apply_wrap::applyTuple( f, t, std::get( t ), args... ); } }; template<> struct apply_wrap<0> { template static R applyTuple( std::function& f, const std::tuple&, UnpackedArgs... args ) { return f( args... ); } }; template R applyTuple( std::function& f, std::tuple const& t ) { return apply_wrap::applyTuple( f, t ); } int fac(int n) { int r=1; for(int i=2; i<=n; ++i) r *= i; return r; } int main() { auto t = std::make_tuple(5); auto f = std::function(&fac); cout << applyTuple(f, t); } 

Qué tal esto:

 // Warning: NOT tested! #include  #include  #include  #include  using std::declval; using std::forward; using std::get; using std::integral_constant; using std::size_t; using std::tuple; namespace detail { template < typename Func, typename ...T, typename ...Args > auto explode_tuple( integral_constant, tuple const &t, Func &&f, Args &&...a ) -> decltype( forward(f)(declval()...) ) { return forward( f )( forward(a)... ); } template < size_t Index, typename Func, typename ...T, typename ...Args > auto explode_tuple( integral_constant, tuple const&t, Func &&f, Args &&...a ) -> decltype( forward(f)(declval()...) ) { return explode_tuple( integral_constant{}, t, forward(f), get(t), forward(a)... ); } } template < typename Func, typename ...T > auto run_tuple( Func &&f, tuple const &t ) -> decltype( forward(f)(declval()...) ) { return detail::explode_tuple( integral_constant{}, t, forward(f) ); } template < typename Tret, typename ...T > Tret func_T( tuple const &t ) { return run_tuple( &func, t ); } 

La plantilla de función run_tuple toma la tupla dada y pasa sus elementos individualmente a la función dada. Lleva a cabo su trabajo llamando recursivamente a sus plantillas de funciones auxiliares explode_tuple . Es importante que run_tuple pase el tamaño de la tupla a explode_tuple ; ese número actúa como un contador de cuántos elementos extraer.

Si la tupla está vacía, run_tuple llama a la primera versión de explode_tuple con la función remota como único argumento. La función remota se llama sin argumentos y terminamos. Si la tupla no está vacía, se pasa un número mayor a la segunda versión de explode_tuple , junto con la función remota. Se realiza una llamada recursiva a explode_tuple , con los mismos argumentos, excepto que el número de contador se reduce en uno y (una referencia a) el último elemento de tupla se agrega como argumento después de la función remota. En una llamada recursiva, el contador no es cero y se realiza otra llamada con el contador disminuido nuevamente y el siguiente elemento sin referencia se inserta en la lista de argumentos después de la función remota pero antes de los otros argumentos insertados, o el contador alcanza cero y se llama a la función remota con todos los argumentos acumulados después de ella.

No estoy seguro de tener la syntax de forzar una versión particular de una plantilla de función correcta. Creo que puede usar un puntero a función como un objeto de función; el comstackdor lo arreglará automáticamente.

Estoy evaluando MSVS 2013RC, y no compiló algunas de las soluciones anteriores propuestas aquí en algunos casos. Por ejemplo, MSVS no comstackrá retornos “automáticos” si hay demasiados parámetros de función, debido a un límite de imbricación del espacio de nombres (envié esa información a Microsoft para que se corrigiera). En otros casos, necesitamos acceso a la devolución de la función, aunque eso también se puede hacer con un lamda: los dos ejemplos siguientes dan el mismo resultado.

 apply_tuple([&ret1](double a){ret1 = cos(a); }, std::make_tuple(.2)); ret2 = apply_tuple((double(*)(double))cos, std::make_tuple(.2)); 

Y gracias de nuevo a aquellos que publicaron respuestas aquí antes que yo, no habría llegado a esto sin ella … así que aquí está:

 template struct apply_impl { template static inline auto apply_tuple(F&& f, T&& t, A&&... a) -> decltype(apply_impl::apply_tuple(std::forward(f), std::forward(t), std::get(std::forward(t)), std::forward(a)...)) { return apply_impl::apply_tuple(std::forward(f), std::forward(t), std::get(std::forward(t)), std::forward(a)...); } template static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a) -> decltype(apply_impl::apply_tuple(o, std::forward(f), std::forward(t), std::get(std::forward(t)), std::forward(a)...)) { return apply_impl::apply_tuple(o, std::forward(f), std::forward(t), std::get(std::forward(t)), std::forward(a)...); } }; // This is a work-around for MSVS 2013RC that is required in some cases #if _MSC_VER <= 1800 /* update this when bug is corrected */ template<> struct apply_impl<6> { template static inline auto apply_tuple(F&& f, T&& t, A&&... a) -> decltype(std::forward(f)(std::get<0>(std::forward(t)), std::get<1>(std::forward(t)), std::get<2>(std::forward(t)), std::get<3>(std::forward(t)), std::get<4>(std::forward(t)), std::get<5>(std::forward(t)), std::forward(a)...)) { return std::forward(f)(std::get<0>(std::forward(t)), std::get<1>(std::forward(t)), std::get<2>(std::forward(t)), std::get<3>(std::forward(t)), std::get<4>(std::forward(t)), std::get<5>(std::forward(t)), std::forward(a)...); } template static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a) -> decltype((o->*std::forward(f))(std::get<0>(std::forward(t)), std::get<1>(std::forward(t)), std::get<2>(std::forward(t)), std::get<3>(std::forward(t)), std::get<4>(std::forward(t)), std::get<5>(std::forward(t)), std::forward(a)...)) { return (o->*std::forward(f))(std::get<0>(std::forward(t)), std::get<1>(std::forward(t)), std::get<2>(std::forward(t)), std::get<3>(std::forward(t)), std::get<4>(std::forward(t)), std::get<5>(std::forward(t)), std::forward(a)...); } }; #endif template<> struct apply_impl<0> { template static inline auto apply_tuple(F&& f, T&&, A&&... a) -> decltype(std::forward(f)(std::forward(a)...)) { return std::forward(f)(std::forward(a)...); } template static inline auto apply_tuple(C*const o, F&& f, T&&, A&&... a) -> decltype((o->*std::forward(f))(std::forward(a)...)) { return (o->*std::forward(f))(std::forward(a)...); } }; // Apply tuple parameters on a non-member or static-member function by perfect forwarding template inline auto apply_tuple(F&& f, T&& t) -> decltype(apply_impl::type>::value>::apply_tuple(std::forward(f), std::forward(t))) { return apply_impl::type>::value>::apply_tuple(std::forward(f), std::forward(t)); } // Apply tuple parameters on a member function template inline auto apply_tuple(C*const o, F&& f, T&& t) -> decltype(apply_impl::type>::value>::apply_tuple(o, std::forward(f), std::forward(t))) { return apply_impl::type>::value>::apply_tuple(o, std::forward(f), std::forward(t)); } 

Ampliando la solución de @David, puede escribir una plantilla recursiva que

  1. No usa la semántica de integer_sequence enteros (demasiado verbosa, imo)
  2. No utiliza un parámetro de plantilla temporal adicional int N para contar iteraciones recursivas
  3. (Opcional para funtores estáticos / globales) utiliza el funtor como parámetro de plantilla para la optimización en tiempo de comstackción

P.ej:

 template  struct static_functor { template  static inline auto apply(const std::tuple& t, Args_tmp... args) -> decltype(func(std::declval()...)) { return static_functor::apply(t, args..., std::get(t)); } template  static inline auto apply(const std::tuple& t, T... args) -> decltype(func(args...)) { return func(args...); } }; static_functor::apply(my_tuple); 

Alternativamente, si su functor no está definido en tiempo de comstackción (por ejemplo, una instancia de functor no constexpr o una expresión lambda), puede usarlo como un parámetro de función en lugar de un parámetro de plantilla de clase, y de hecho eliminar completamente la clase contenedora :

 template  inline auto apply_functor(F&& func, const std::tuple& t, Args_tmp... args) -> decltype(func(std::declval()...)) { return apply_functor(func, t, args..., std::get(t)); } template  inline auto apply_functor(F&& func, const std::tuple& t, T... args) -> decltype(func(args...)) { return func(args...); } apply_functor(&myFunc, my_tuple); 

Para callables de puntero a miembro, puede ajustar cualquiera de los fragmentos de código anteriores de forma similar a la respuesta de @David.

Explicación

En referencia a la segunda parte de código, hay dos funciones de plantilla: la primera toma el funcctor func , la tupla t con tipos T... , y un paquete de parámetros args de tipos Args_tmp... Cuando se llama, agrega recursivamente los objetos de t al paquete de parámetros de a uno por vez, desde el comienzo ( 0 ) hasta el final, y llama a la función nuevamente con el nuevo paquete de parámetros incrementado.

La firma de la segunda función es casi idéntica a la primera, excepto que utiliza el tipo T... para el parámetro pack args . Por lo tanto, una vez que args en la primera función se llena por completo con los valores de t , su tipo será T... (en psuedo-code, typeid(T...) == typeid(Args_tmp...) ), y por lo tanto, el comstackdor llamará a la segunda función sobrecargada, que a su vez llama a func(args...) .

El código en el ejemplo de functor estático funciona de manera idéntica, y el functor se usa como argumento de plantilla de clase.

Why not just wrap your variadic arguments into a tuple class and then use compile time recursion (see link ) to retrieve the index you are interested in. I find that unpacking variadic templates into a container or collection may not be type safe wrt heterogeneous types

 template auto get_args_as_tuple(Args... args) -> std::tuple { return std::make_tuple(args); } 

This simple solution works for me:

 template void unwrap_tuple(std::tuple* tp) { std::cout << "And here I have the tuple types, all " << sizeof...(T) << " of them" << std::endl; } int main() { using TupleType = std::tuple; unwrap_tuple((TupleType*)nullptr); // trick compiler into using template param deduction }