¿Cómo se pueden iterar sobre los elementos de un std::tuple?

Resuelto asked hace 15 años • 24 respuestas

¿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 puedo iterar correctamente sobre los elementos de una tupla?

 avatar Jul 29 '09 12:07
Aceptado

Tengo una respuesta basada en Iterar sobre una tupla :

#include <tuple>
#include <utility> 
#include <iostream>

template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
  print(std::tuple<Tp...>& t)
  { }

template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
  print(std::tuple<Tp...>& t)
  {
    std::cout << std::get<I>(t) << std::endl;
    print<I + 1, Tp...>(t);
  }

int
main()
{
  typedef std::tuple<int, float, double> T;
  T t = std::make_tuple(2, 3.14159F, 2345.678);

  print(t);
}

La idea habitual es utilizar la recursividad en tiempo de compilación. De hecho, esta idea se utiliza para realizar una impresión con seguridad tipográfica, como se indica en los documentos de tupla originales.

Esto se puede generalizar fácilmente en un for_eachfor tuplas:

#include <tuple>
#include <utility> 

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
  for_each(std::tuple<Tp...> &, FuncT) // Unused arguments are given no names.
  { }

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
  for_each(std::tuple<Tp...>& t, FuncT f)
  {
    f(std::get<I>(t));
    for_each<I + 1, FuncT, Tp...>(t, f);
  }

Aunque esto requiere algo de esfuerzo para FuncTrepresentar algo con las sobrecargas adecuadas para cada tipo que pueda contener la tupla. Esto funciona mejor si sabes que todos los elementos de la tupla compartirán una clase base común o algo similar.

emsr avatar Aug 01 '2011 05:08 emsr

En C++ 17, puedes usar std::applycon expresión de plegado :

std::apply([](auto&&... args) {((/* args.dosomething() */), ...);}, the_tuple);

Un ejemplo completo para imprimir una tupla:

#include <tuple>
#include <iostream>

int main()
{
    std::tuple t{42, 'a', 4.2}; // Another C++17 feature: class template argument deduction
    std::apply([](auto&&... args) {((std::cout << args << '\n'), ...);}, t);
}

[Ejemplo en línea sobre Coliru]

Esta solución resuelve la cuestión del orden de evaluación en la respuesta de M. Alaggan .

xskxzr avatar Jan 05 '2019 14:01 xskxzr

C++ está introduciendo declaraciones de expansión para este propósito. Originalmente estaban en camino para C++20, pero por poco no lograron el corte debido a la falta de tiempo para revisar la redacción del lenguaje (ver aquí y aquí ).

La sintaxis actualmente acordada (consulte los enlaces anteriores) es:

{
    auto tup = std::make_tuple(0, 'a', 3.14);
    template for (auto elem : tup)
        std::cout << elem << std::endl;
}
Daniel Steck avatar Jun 27 '2019 10:06 Daniel Steck

Boost.Fusion es una posibilidad:

Ejemplo no probado:

struct DoSomething
{
    template<typename T>
    void operator()(T& t) const
    {
        t.do_sth();
    }
};

tuple<....> t = ...;
boost::fusion::for_each(t, DoSomething());
Éric Malenfant avatar Jul 29 '2009 17:07 Éric Malenfant