Desempaquetando argumentos de tuplas

Así que estoy tratando de averiguar cómo funciona esto: C ++ 11: puedo pasar de múltiples args a tuplas, pero ¿puedo pasar de una tupla a múltiples args?

La pieza de magia negra que no entiendo es este fragmento de código:

f(std::get(std::forward(t))...) 

Es la expresión interior que no entiendo.

Entiendo que la expresión de alguna manera desempaqueta / expande lo que está dentro de t en una lista de argumentos. ¿Pero a alguien le podría importar cómo se hace esto? Cuando miro la definición de std::get ( http://en.cppreference.com/w/cpp/utility/tuple/get ), no veo cómo encaja N en …? Por lo que puedo decir, N es una secuencia de números enteros.

Según lo que puedo observar, asumo que las expresiones en la forma E... donde X es la secuencia de los tipos X1 . X2 , … Xn , la expresión se expandirá como E, E ... E . ¿Así es como funciona?

Edición : En este caso, N no es una secuencia de tipos, sino números enteros. Pero supongo que esta construcción de lenguaje se aplica tanto a los tipos como a los valores.

Creo que el comentario de @Xeo lo resumió bien. A partir del 14.5.3 del estándar C ++ 11:

La expansión de un paquete consiste en un patrón y una elipsis , cuya creación genera cero o más instancias del patrón en una lista.

En su caso, para cuando termine con la creación de instancias recursiva de la plantilla y termine en la especialización parcial, tendrá

 f(std::get(std::forward(t))...); 

… donde N es el paquete de parámetros de cuatro int s ( 0 , 1 , 2 y 3 ). A partir de la norma anterior, el patrón aquí es

 std::get(std::forward(t)) 

La aplicación de ... puntos suspensivos al patrón anterior hace que se expanda en cuatro instancias en forma de lista, es decir

 f(std::get<0>(t), std::get<1>(t), std::get<2>(t), std::get<3>(t)); 

El ingrediente fundamental para expandir std::tuple realidad se omite en el código: debe obtener un segundo parámetro de vuelta: además de la lista de tipos de std::tuple<...> necesita un paquete de parámetros con índices 0, 1, ..., n . Una vez que tenga estos dos paquetes de parámetros, puede expandirlos en tándem:

 template  void call_impl(F&& fun, std::tuple&& t) { fun(std::get(t)...); } 

La verdadera magia reside en conjurar el segundo paquete de parámetros cuando solo tienes una std::tuple . Se necesita un poco de progtwigción de plantillas. Aquí hay un enfoque para crear la lista de índices:

 template  struct indices; template <> struct indices<-1> { typedef indices<> type; }; template  struct indices<0, Indices...> { typedef indices<0, Indices...> type; }; template  struct indices { typedef typename indices::type type; }; template  typename indices::value - 1>::type const* make_indices() { return 0; } 

Entonces, si tiene una plantilla de función, llamémosla call() que toma un objeto de función y un std::tuple con los argumentos de la función. Un enfoque fácil es reescribir el call_impl() mencionado anteriormente para tratar de deducir los índices:

 template  void call_impl(F&& fun, Tuple&& t, indices const*) { fun(std::get(t)...); } template  void call(F&& fun, Tuple&& t) { call_imle(std::forward(fun), std::forward(t), make_indices()); } 

Lo que este código no extiende realmente es el uso correcto de std::forward<...>() con los diversos elementos std::tuple<...> al llamar a la función. El solo uso de std::forward(t) no funciona porque posiblemente mueve todo el std::tuple<...> lugar de mover los elementos. Creo que se puede hacer algo así como un movimiento adecuado de elementos de un std::tuple<...> pero todavía no lo he hecho.