Considere el caso de una función con plantilla con argumentos de plantilla variadic:
template<typename Tret, typename... T> Tret func(const T&... t);
Ahora tengo una tupla t
de valores. ¿Cómo llamo func()
usando los valores de tupla como argumentos? He leído sobre el bind()
objeto de función, con call()
función, y también la apply()
función en diferentes documentos obsoletos. La implementación de GNU GCC 4.4 parece tener una call()
función en la bind()
clase, 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 plantilla variadic es poder usarlos en casos como el anterior.
¿Alguien tiene una solución a is, o insinúa dónde leer sobre esto?
integer_sequence
, vea en.cppreference.com/w/cpp/utility/integer_sequenceinteger_sequence S
, simplemente llama a su función comofunc(std::get<S>(tuple)...)
y deja que el compilador se encargue del resto.Respuestas:
Aquí está mi código si alguien está interesado
Básicamente, en el momento de la compilación, el compilador desenrolla recursivamente todos los argumentos en varias llamadas de función inclusivas <N> -> llamadas <N-1> -> llamadas ... -> llamadas <0>, que es la última y el compilador se optimizará. las diversas llamadas a funciones intermedias solo mantienen la última que es el equivalente de func (arg1, arg2, arg3, ...)
Se proporcionan 2 versiones, una para una función invocada en un objeto y la otra para una función estática.
fuente
tr1
cosas se pueden sacar ahora con c ++ 11En C ++ 17 puedes hacer esto:
Esto ya funciona en Clang ++ 3.9, usando std :: experimental :: apply.
Respondiendo al comentario que dice que esto no funcionará si
the_function
tiene una plantilla, lo siguiente es una solución alternativa:Esta solución alternativa es una solución simplificada al problema general de pasar conjuntos de sobrecarga y plantillas de funciones donde se esperaría una función. La solución general (una que se ocupa del reenvío perfecto, constexpr-ness y noexcept-ness) se presenta aquí: https://blog.tartanllama.xyz/passing-overload-sets/ .
fuente
the_function
tiene una plantilla.std::apply(add_generic<float>, std::make_pair(2.0f, 3.0f));
En C ++ hay muchas formas de expandir / desempaquetar tuplas y aplicar esos elementos de tupla a una función de plantilla variable. Aquí hay una pequeña clase auxiliar que crea una matriz de índice. Se usa mucho en la metaprogramación de plantillas:
Ahora el código que hace el trabajo no es tan grande:
La prueba se muestra a continuación:
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 ...
fuente
template<class ... T> void three(T...) {}
e intento usar apply, no se compila.Creo que esta es la solución más elegante (y se reenvía de manera óptima):
Ejemplo de uso:
Desafortunadamente, GCC (4.6 al menos) no puede compilar esto con "lo siento, sin implementar: sobrecarga de manipulación" (lo que simplemente significa que el compilador aún no implementa completamente la especificación C ++ 11), y dado que usa plantillas variadas, no funciona en MSVC, por lo que es más o menos inútil. Sin embargo, una vez que haya un compilador que admita la especificación, será el mejor enfoque en mi humilde opinión. (Nota: no es tan difícil modificar esto para poder solucionar las deficiencias en GCC, o implementarlo con el preprocesador Boost, pero arruina la elegancia, así que esta es la versión que estoy publicando).GCC 4.7 ahora admite este código perfectamente.
Editar: se agregó hacia adelante alrededor de la llamada a la función real para admitir el formulario de referencia rvalue * esto en caso de que esté usando clang (o si alguien más realmente llega a agregarlo).
Editar: se agregó la falta hacia adelante alrededor del objeto de función en el cuerpo de la función de aplicación no miembro. Gracias a pheedbaq por señalar que faltaba.
Editar: Y aquí está la versión C ++ 14 solo porque es mucho mejor (en realidad aún no se compila):
Aquí hay una versión para funciones miembro (¡no se ha probado mucho!):
fuente
apply
, ¿por quéf
no se envuelve con unastd::forward
llamada, ya que está en el tipo de retorno? ¿No es necesario?foo('x', true)
compilé exactamente el mismo código de ensamblaje queapply(foo, ::std::make_tuple('x', true))
con cualquier nivel de optimización además de -O0.integer_sequence
incluso obtienes una implementación casi correctaapply()
en su ejemplo. mira mi respuesta a continuación.Esto está adaptado del borrador de C ++ 14 usando index_sequence. Podría proponer que se aplique en un futuro estándar (TS).
fuente
La noticia no se ve bien.
Después de leer el borrador del estándar recién publicado , no veo una solución integrada para esto, lo que parece extraño.
El mejor lugar para preguntar sobre estas cosas (si aún no lo ha hecho) es comp.lang.c ++. Moderado, porque algunas personas involucradas en la redacción 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á que esta respuesta completa es un poco frustrante!), Y se sugieren algunas implementaciones desagradables.
Me preguntaba si sería más simple hacer que la función acepte a
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 máxima flexibilidad, y de modo que solo demuestre la extrañeza de no proporcionar una expansión incorporada de tupla para el paquete de argumentos de función.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
fuente
Todas estas implementaciones son buenas. Pero debido al uso del puntero al compilador de funciones miembro, a menudo no se puede alinear la llamada a la función de destino (al menos gcc 4.8 no se puede, no importa qué. ¿Por qué gcc no se pueden determinar los 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 miembro como argumentos de plantilla, no como parámetros de función:
Y uso:
Prueba de inlinable http://goo.gl/5UqVnC
Con pequeños cambios, podemos "sobrecargar"
apply_tuple
:Además, esta es la única solución que funciona con funciones de plantilla.
fuente
1) si tiene una estructura ready_meter parameter_pack como argumento de función, puede usar std :: tie así:
2) si no tienes un argumento de parampack listo, tendrás que desenrollar la tupla así
fuente
Qué tal esto:
La
run_tuple
plantilla de función 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 función auxiliarexplode_tuple
. Es importante querun_tuple
pase el tamaño de la tupla aexplode_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 deexplode_tuple
con la función remota como el único otro argumento. La función remota se llama sin argumentos y hemos terminado. Si la tupla no está vacía, se pasa un número mayor a la segunda versión deexplode_tuple
, junto con la función remota. Una llamada recursiva aexplode_tuple
se realiza, 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 la función remota se llama con todos los argumentos acumulados después de ella.No estoy seguro de tener la sintaxis de forzar una versión particular de una plantilla de función correcta. Creo que puede usar un puntero para funcionar como un objeto de función; el compilador lo arreglará automáticamente.
fuente
Estoy evaluando MSVS 2013RC, y no pudo compilar algunas de las soluciones anteriores propuestas aquí en algunos casos. Por ejemplo, MSVS no podrá compilar devoluciones "automáticas" si hay demasiados parámetros de función, debido a un límite de imbricación de espacio de nombres (envié esa información a Microsoft para que se corrigiera). En otros casos, necesitamos acceso al retorno de la función, aunque eso también se puede hacer con una lamda: los siguientes dos ejemplos dan el mismo resultado.
Y gracias nuevamente a aquellos que publicaron respuestas aquí antes que yo, no habría llegado a esto sin él ... así que aquí está:
fuente
const
?Extendiendo la solución de @ David, puede escribir una plantilla recursiva que
integer_sequence
semánticaint N
para contar iteraciones recursivasP.ej:
Alternativamente, si su functor no está definido en tiempo de compilación (por ejemplo, una
constexpr
instancia que no sea functor 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 que lo contiene:Para los invocables de la función de puntero a miembro, puede ajustar cualquiera de las piezas de código anteriores de manera similar a la respuesta de @ David.
Explicación
En referencia al segundo fragmento de código, hay dos funciones de plantilla: la primera toma el functor
func
, la tuplat
con tiposT...
y un paquete de parámetrosargs
de tipos deArgs_tmp...
. Cuando se le llama, agrega recursivamente los objetos delt
paquete de parámetros uno a la vez, desde el principio (0
) hasta el final, y vuelve a llamar a la función con el nuevo paquete de parámetros incrementado.La firma de la segunda función es casi idéntica a la primera, excepto que usa el tipo
T...
para el paquete de parámetrosargs
. Por lo tanto, una vezargs
en la primera función se llena por completo con los valores det
, su tipo seráT...
(en psuedo-code,typeid(T...) == typeid(Args_tmp...)
) y, por lo tanto, el compilador llamará a la segunda función sobrecargada, que a su vez llamafunc(args...)
.El código en el ejemplo del functor estático funciona de manera idéntica, y el functor se usa como argumento de plantilla de clase.
fuente
¿Por qué no simplemente envolver sus argumentos variadic en una clase de tupla y luego usar la recursión en tiempo de compilación (vea el enlace ) para recuperar el índice que le interesa? Me parece que desempaquetar plantillas variadic en un contenedor o colección puede no ser de tipo seguro con tipos heterogéneos
fuente
Args...
->tuple
, perotuple
->Args...
.Esta solución simple me funciona:
fuente