¿Cómo puedo iterar sobre una tupla (usando C ++ 11)? Intenté lo siguiente:
for(int i=0; i<std::tuple_size<T...>::value; ++i)
std::get<i>(my_tuple).do_sth();
pero esto no funciona:
Error 1: lo siento, no implementado: no se puede expandir 'Oyente ...' en una lista de argumentos de longitud fija.
Error 2: no puedo aparecer en una expresión constante.
Entonces, ¿cómo iterar correctamente sobre los elementos de una tupla?
Respuestas:
Boost.Fusion es una posibilidad:
Ejemplo no probado:
fuente
Tengo una respuesta basada en iterar sobre una tupla :
La idea habitual es utilizar la recursividad en tiempo de compilación. De hecho, esta idea se utiliza para hacer un printf que sea seguro para tipos como se indica en los papeles de tupla originales.
Esto se puede generalizar fácilmente en una
for_each
para tuplas:Aunque esto requiere un poco de esfuerzo para
FuncT
representar algo con las sobrecargas apropiadas para cada tipo que la tupla pueda contener. Esto funciona mejor si sabe que todos los elementos de la tupla compartirán una clase base común o algo similar.fuente
enable_if
documentación .for_each
. De hecho, lo hice yo mismo. :-) Creo que esta respuesta sería más útil si ya estuviera generalizada.const std::tuple<Tp...>&
.. Si no tiene la intención de modificar tuplas mientras itera, esasconst
versiones serán suficientes.En C ++ 17, puede usar
std::apply
con expresión de plegado :Un ejemplo completo para imprimir una tupla:
[Ejemplo en línea sobre Coliru]
Esta solución resuelve el problema del orden de evaluación en la respuesta de M. Alaggan .
fuente
((std::cout << args << '\n'), ...);
:? La lambda se invoca una vez con los elementos de tupla desempaquetados comoargs
, pero ¿qué pasa con los paréntesis dobles?((std::cout << arg1 << '\n'), (std::cout << arg2 << '\n'), (std::cout << arg3 << '\n'))
.En C ++ 17 puedes hacer esto:
Esto ya funciona en Clang ++ 3.9, usando std :: experimental :: apply.
fuente
do_something()
, se produzcan en un orden no especificado, porque el paquete de parámetros se expande dentro de una llamada de función()
, donde los argumentos tienen un orden no especificado? Eso podría ser muy significativo; Me imagino que la mayoría de la gente esperaría que se garantice que la ordenación ocurrirá en el mismo orden que los miembros, es decir, como los índicesstd::get<>()
. AFAIK, para obtener pedidos garantizados en casos como este, la expansión debe hacerse dentro{braces}
. ¿Me equivoco? Esta respuesta pone énfasis en dichos pedidos: stackoverflow.com/a/16387374/2757035Utilice Boost.Hana y lambdas genéricas:
http://coliru.stacked-crooked.com/a/27b3691f55caf271
fuente
using namespace boost::fusion
(especialmente junto conusing namespace std
). Ahora no hay forma de saber si esofor_each
esstd::for_each
oboost::fusion::for_each
C ++ está introduciendo declaraciones de expansión para este propósito. Originalmente iban por buen camino para C ++ 20, pero fallaron por poco debido a la falta de tiempo para revisar la redacción del lenguaje (ver aquí y aquí ).
La sintaxis acordada actualmente (consulte los enlaces anteriores) es:
fuente
Una forma más simple, intuitiva y amigable para el compilador de hacer esto en C ++ 17, usando
if constexpr
:Esta es la recursividad en tiempo de compilación, similar a la presentada por @emsr. Pero esto no usa SFINAE, así que (creo) es más amigable para el compilador.
fuente
Necesita usar la metaprogramación de plantilla, aquí se muestra con Boost.Tuple:
En C ++ 0x, puede escribir
print_tuple()
como una función de plantilla variada.fuente
Primero defina algunos ayudantes de índice:
Con su función le gustaría aplicar en cada elemento de tupla:
puedes escribir:
O si
foo
regresavoid
, useNota: En C ++ 14
make_index_sequence
ya está definido ( http://en.cppreference.com/w/cpp/utility/integer_sequence ).Si necesita un orden de evaluación de izquierda a derecha, considere algo como esto:
fuente
foo
tovoid
antes de invocaroperator,
para evitar una posible sobrecarga del operador patológico.Aquí hay una forma fácil de C ++ 17 de iterar sobre elementos de tupla con solo una biblioteca estándar:
Ejemplo:
Salida:
Esto se puede extender para romper condicionalmente el bucle en caso de que el invocable devuelva un valor (pero aún funciona con invocables que no devuelven un valor bool asignable, por ejemplo, void):
Ejemplo:
Salida:
fuente
Si desea utilizar std :: tuple y tiene un compilador de C ++ que admite plantillas variadic, pruebe el siguiente código (probado con g ++ 4.5). Esta debería ser la respuesta a tu pregunta.
boost :: fusion es otra opción, pero requiere su propio tipo de tupla: boost :: fusion :: tuple. ¡Es mejor que nos ciñamos al estándar! Aquí hay una prueba:
¡el poder de las plantillas variadas!
fuente
En MSVC STL hay una función _For_each_tuple_element (no documentada):
fuente
Otros han mencionado algunas bibliotecas de terceros bien diseñadas a las que puede recurrir. Sin embargo, si está utilizando C ++ sin esas bibliotecas de terceros, el siguiente código puede ayudar.
Nota: el código se compila con cualquier compilador compatible con C ++ 11 y mantiene la coherencia con el diseño de la biblioteca estándar:
La tupla no tiene por qué serlo
std::tuple
y, en cambio, puede ser cualquier cosa que admitastd::get
ystd::tuple_size
; en particular,std::array
ystd::pair
puede usarse;La tupla puede ser un tipo de referencia o calificada por cv;
Tiene un comportamiento similar
std::for_each
y devuelve la entradaUnaryFunction
;Para usuarios de C ++ 14 (o versión más reciente),
typename std::enable_if<T>::type
ytypename std::decay<T>::type
podría reemplazarse con su versión simplificada,std::enable_if_t<T>
ystd::decay_t<T>
;Para los usuarios de C ++ 17 (laster versión o),
std::tuple_size<T>::value
podría ser reemplazado con su versión simplificada,std::tuple_size_v<T>
.Para los usuarios de C ++ 20 (o la versión más reciente), la
SFINAE
función podría implementarse conConcepts
.fuente
Usar
constexpr
yif constexpr
(C ++ 17) esto es bastante simple y directo:fuente
Puede que haya perdido este tren, pero estará aquí para referencia futura.
Aquí está mi construcción basada en esta respuesta y en esta esencia :
Luego lo usa de la siguiente manera:
Podría haber margen de mejora.
Según el código de OP, se convertiría en esto:
fuente
De todas las respuestas que he visto aquí, aquí y aquí , me gustó más la forma de iterar de @sigidagi. Desafortunadamente, su respuesta es muy detallada, lo que en mi opinión oscurece la claridad inherente.
Esta es mi versión de su solución, que es más concisa y trabaja con
std::tuple
,std::pair
ystd::array
.Demostración: coliru
C ++ 14
std::make_index_sequence
se puede implementar para C ++ 11 .fuente
tupla de impulso proporciona funciones de ayuda
get_head()
yget_tail()
por lo que sus funciones de ayuda pueden tener este aspecto:como se describe aquí http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html
con
std::tuple
él debería ser similar.En realidad, desafortunadamente
std::tuple
no parece proporcionar dicha interfaz, por lo que los métodos sugeridos antes deberían funcionar, o necesitaría cambiar a losboost::tuple
que tienen otros beneficios (como los operadores io ya proporcionados). Aunque hay una desventaja deboost::tuple
gcc: todavía no acepta plantillas variadas, pero es posible que ya esté arreglado porque no tengo la última versión de boost instalada en mi máquina.fuente
Me he encontrado con el mismo problema para iterar sobre una tupla de objetos de función, así que aquí hay una solución más:
Salida:
fuente
Otra opción sería implementar iteradores para tuplas. Esto tiene la ventaja de que puede utilizar una variedad de algoritmos proporcionados por la biblioteca estándar y bucles for basados en rangos. Un enfoque elegante para esto se explica aquí https://foonathan.net/2017/03/tuple-iterator/ . La idea básica es convertir tuplas en una gama con
begin()
yend()
métodos para proporcionar iteradores. El propio iterador devuelve unstd::variant<...>
que luego se puede visitar usandostd::visit
.Aquí algunos ejemplos:
Mi implementación (que se basa en gran medida en las explicaciones en el enlace anterior):
También se admite el acceso de solo lectura pasando un
const std::tuple<>&
ato_range()
.fuente
Ampliando la respuesta de @Stypox, podemos hacer que su solución sea más genérica (C ++ 17 en adelante). Añadiendo un argumento de función invocable:
Entonces, necesitamos una estrategia para visitar cada tipo.
Comencemos con algunos ayudantes (los dos primeros tomados de cppreference):
variant_ref
se utiliza para permitir que se modifique el estado de las tuplas.Uso:
Resultado:
Para completar, aquí están mis
Bar
&Foo
:fuente