Pregunta

¿Cómo puedo iterar sobre una tupla (usando C ++ 11)? He intentado lo siguiente:

for(int i=0; i<std::tuple_size<T...>::value; ++i) 
  std::get<i>(my_tuple).do_sth();

pero esto no funciona:

  

1 Error: Lo sentimos, no se han aplicado:. No puede expandirse ‘Listener ...’ en una lista de argumentos de longitud fija
  Error 2:. I no puede aparecer en una expresión constante

Así que, ¿cómo correctamente iterar sobre los elementos de un par de valores?

¿Fue útil?

Solución

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());

Otros consejos

Tengo una respuesta basada en iteración más de 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 compilar tiempo. De hecho, esta idea se utiliza para hacer un printf que es un tipo seguro como se señala en los documentos originales de tupla.

Esto se puede generalizar fácilmente en un for_each de 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);
  }

A pesar de esto, entonces requiere un poco de esfuerzo para tener FuncT representar algo con las sobrecargas apropiadas para cada tipo de la tupla puede contener. Esto funciona mejor si usted sabe todos los elementos de tupla compartirá una clase base común o algo similar.

En C ++ 17, puede utilizar std::apply con doblar expresión :

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

Un ejemplo completo para la impresión de 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 Online en Coliru]

Esta solución resuelve el problema de la orden de evaluación en M. respuesta de Alaggan.

Uso Boost.Hana y lambdas genérica:

#include <tuple>
#include <iostream>
#include <boost/hana.hpp>
#include <boost/hana/ext/std/tuple.hpp>

struct Foo1 {
    int foo() const { return 42; }
};

struct Foo2 {
    int bar = 0;
    int foo() { bar = 24; return bar; }
};

int main() {
    using namespace std;
    using boost::hana::for_each;

    Foo1 foo1;
    Foo2 foo2;

    for_each(tie(foo1, foo2), [](auto &foo) {
        cout << foo.foo() << endl;
    });

    cout << "foo2.bar after mutation: " << foo2.bar << endl;
}

http://coliru.stacked-crooked.com/a/27b3691f55caf271

En C ++ 17 se puede hacer esto:

std::apply([](auto ...x){std::make_tuple(x.do_something()...);} , the_tuple);

Esto ya funciona en Clang ++ 3.9, utilizando std :: :: experimental de aplicación.

Es necesario utilizar metaprogramming plantilla, aquí mostrado con Boost.Tuple:

#include <boost/tuple/tuple.hpp>
#include <iostream>

template <typename T_Tuple, size_t size>
struct print_tuple_helper {
    static std::ostream & print( std::ostream & s, const T_Tuple & t ) {
        return print_tuple_helper<T_Tuple,size-1>::print( s, t ) << boost::get<size-1>( t );
    }
};

template <typename T_Tuple>
struct print_tuple_helper<T_Tuple,0> {
    static std::ostream & print( std::ostream & s, const T_Tuple & ) {
        return s;
    }
};

template <typename T_Tuple>
std::ostream & print_tuple( std::ostream & s, const T_Tuple & t ) {
    return print_tuple_helper<T_Tuple,boost::tuples::length<T_Tuple>::value>::print( s, t );
}

int main() {

    const boost::tuple<int,char,float,char,double> t( 0, ' ', 2.5f, '\n', 3.1416 );
    print_tuple( std::cout, t );

    return 0;
}

En C ++ 0x, puede escribir print_tuple() como una función de plantilla variadic lugar.

C ++ está introduciendo declaraciones de expansión para este propósito. Originalmente fueron a la pista para C ++ 20, pero estuvo a punto de corte debido a la falta de tiempo para la revisión lenguaje redacción (ver aquí y aquí ).

La sintaxis momento acordado (ver enlace de arriba) es:

{
    auto tup = std::make_tuple(0, 'a', 3.14);
    template for (auto elem : tup)
        std::cout << elem << std::endl;
}

En primer lugar definir algunos ayudantes índice:

template <size_t ...I>
struct index_sequence {};

template <size_t N, size_t ...I>
struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {};

template <size_t ...I>
struct make_index_sequence<0, I...> : public index_sequence<I...> {};

Con la función que desea aplicar en cada elemento de la tupla:

template <typename T>
/* ... */ foo(T t) { /* ... */ }

puede escribir:

template<typename ...T, size_t ...I>
/* ... */ do_foo_helper(std::tuple<T...> &ts, index_sequence<I...>) {
    std::tie(foo(std::get<I>(ts)) ...);
}

template <typename ...T>
/* ... */ do_foo(std::tuple<T...> &ts) {
    return do_foo_helper(ts, make_index_sequence<sizeof...(T)>());
}

O si foo vuelve void, utilice

std::tie((foo(std::get<I>(ts)), 1) ... );

Nota: En C ++ 14 make_index_sequence ya está definido ( http: // en. cppreference.com/w/cpp/utility/integer_sequence ).

Si usted no necesita una orden de evaluación de izquierda a derecha, considerar algo como esto:

template <typename T, typename ...R>
void do_foo_iter(T t, R ...r) {
    foo(t);
    do_foo(r...);
}

void do_foo_iter() {}

template<typename ...T, size_t ...I>
void do_foo_helper(std::tuple<T...> &ts, index_sequence<I...>) {
    do_foo_iter(std::get<I>(ts) ...);
}

template <typename ...T>
void do_foo(std::tuple<T...> &ts) {
    do_foo_helper(ts, make_index_sequence<sizeof...(T)>());
}

Una forma más sencilla, intuitiva y amigable compilador de hacer esto en C ++ 17, usando if constexpr :

// prints every element of a tuple
template<size_t I = 0, typename... Tp>
void print(std::tuple<Tp...>& t) {
    std::cout << std::get<I>(t) << " ";
    // do things
    if constexpr(I+1 != sizeof...(Tp))
        print<I+1>(t);
}

Esta es la recursividad en tiempo de compilación, similar a la presentada por @emsr. Pero esto no utiliza de manera SFINAE (creo) es más compilador de usar.

Si desea utilizar std :: tupla y tiene compilador de C ++ que soporta plantillas variadic, intente código de abajo (probado con g ++ 4.5). Esta debe ser la respuesta a su pregunta.

#include <tuple>

// ------------- UTILITY---------------
template<int...> struct index_tuple{}; 

template<int I, typename IndexTuple, typename... Types> 
struct make_indexes_impl; 

template<int I, int... Indexes, typename T, typename ... Types> 
struct make_indexes_impl<I, index_tuple<Indexes...>, T, Types...> 
{ 
    typedef typename make_indexes_impl<I + 1, index_tuple<Indexes..., I>, Types...>::type type; 
}; 

template<int I, int... Indexes> 
struct make_indexes_impl<I, index_tuple<Indexes...> > 
{ 
    typedef index_tuple<Indexes...> type; 
}; 

template<typename ... Types> 
struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...> 
{}; 

// ----------- FOR EACH -----------------
template<typename Func, typename Last>
void for_each_impl(Func&& f, Last&& last)
{
    f(last);
}

template<typename Func, typename First, typename ... Rest>
void for_each_impl(Func&& f, First&& first, Rest&&...rest) 
{
    f(first);
    for_each_impl( std::forward<Func>(f), rest...);
}

template<typename Func, int ... Indexes, typename ... Args>
void for_each_helper( Func&& f, index_tuple<Indexes...>, std::tuple<Args...>&& tup)
{
    for_each_impl( std::forward<Func>(f), std::forward<Args>(std::get<Indexes>(tup))...);
}

template<typename Func, typename ... Args>
void for_each( std::tuple<Args...>& tup, Func&& f)
{
   for_each_helper(std::forward<Func>(f), 
                   typename make_indexes<Args...>::type(), 
                   std::forward<std::tuple<Args...>>(tup) );
}

template<typename Func, typename ... Args>
void for_each( std::tuple<Args...>&& tup, Func&& f)
{
   for_each_helper(std::forward<Func>(f), 
                   typename make_indexes<Args...>::type(), 
                   std::forward<std::tuple<Args...>>(tup) );
}

impulso :: fusión es otra opción, pero requiere su propio tipo tupla: impulsar la fusión :: :: tupla. Permite una mejor palo para el estándar! Aquí está una prueba:

#include <iostream>

// ---------- FUNCTOR ----------
struct Functor 
{
    template<typename T>
    void operator()(T& t) const { std::cout << t << std::endl; }
};

int main()
{
    for_each( std::make_tuple(2, 0.6, 'c'), Functor() );
    return 0;
}

el poder de las plantillas variadic!

Esta es una forma fácil de C ++ 17 modo de iterar sobre los elementos de tupla con la biblioteca solo estándar:

#include <tuple>      // std::tuple
#include <functional> // std::invoke

template <
    size_t Index = 0, // start iteration at 0 index
    typename TTuple,  // the tuple type
    size_t Size =
        std::tuple_size_v<
            std::remove_reference_t<TTuple>>, // tuple size
    typename TCallable, // the callable to bo invoked for each tuple item
    typename... TArgs   // other arguments to be passed to the callable 
>
void for_each(TTuple&& tuple, TCallable&& callable, TArgs&&... args)
{
    if constexpr (Index < Size)
    {
        std::invoke(callable, args..., std::get<Index>(tuple));

        if constexpr (Index + 1 < Size)
            for_each<Index + 1>(
                std::forward<TTuple>(tuple),
                std::forward<TCallable>(callable),
                std::forward<TArgs>(args)...);
    }
}

Ejemplo:

#include <iostream>

int main()
{
    std::tuple<int, char> items{1, 'a'};
    for_each(items, [](const auto& item) {
        std::cout << item << "\n";
    });
}

Salida:

1
a

Esto se puede ampliar para romper condicionalmente el bucle en caso de que no desembolsadas devuelve un valor (pero aún trabajar con callables que no devuelven un valor asignable bool, por ejemplo void):

#include <tuple>      // std::tuple
#include <functional> // std::invoke

template <
    size_t Index = 0, // start iteration at 0 index
    typename TTuple,  // the tuple type
    size_t Size =
    std::tuple_size_v<
    std::remove_reference_t<TTuple>>, // tuple size
    typename TCallable, // the callable to bo invoked for each tuple item
    typename... TArgs   // other arguments to be passed to the callable 
    >
    void for_each(TTuple&& tuple, TCallable&& callable, TArgs&&... args)
{
    if constexpr (Index < Size)
    {
        if constexpr (std::is_assignable_v<bool&, std::invoke_result_t<TCallable&&, TArgs&&..., decltype(std::get<Index>(tuple))>>)
        {
            if (!std::invoke(callable, args..., std::get<Index>(tuple)))
                return;
        }
        else
        {
            std::invoke(callable, args..., std::get<Index>(tuple));
        }

        if constexpr (Index + 1 < Size)
            for_each<Index + 1>(
                std::forward<TTuple>(tuple),
                std::forward<TCallable>(callable),
                std::forward<TArgs>(args)...);
    }
}

Ejemplo:

#include <iostream>

int main()
{
    std::tuple<int, char> items{ 1, 'a' };
    for_each(items, [](const auto& item) {
        std::cout << item << "\n";
    });

    std::cout << "---\n";

    for_each(items, [](const auto& item) {
        std::cout << item << "\n";
        return false;
    });
}

Salida:

1
a
---
1

tupla de impulso proporciona funciones de ayuda y get_head() get_tail() por lo que sus funciones de ayuda puede tener este aspecto:

inline void call_do_sth(const null_type&) {};

template <class H, class T>
inline void call_do_sth(cons<H, T>& x) { x.get_head().do_sth(); call_do_sth(x.get_tail()); }

como se describe aquí http: // www .boost.org / doc / libs / 1_34_0 / libs / tupla / doc / tuple_advanced_interface.html

con std::tuple que debe ser similar.

En realidad, por desgracia, no parece std::tuple proporcionar dicha interfaz, por lo que los métodos sugeridos antes deben trabajar, o que tendrían que cambiar a boost::tuple que tiene otros beneficios (como operadores io que se proporciona). Aunque no hay inconveniente de boost::tuple con gcc -. No acepta plantillas variadic aún, pero que puede ser fijo ya que no tengo última versión del impulso instalado en mi máquina

que podría haber perdido este tren, pero esta será aquí para referencia futura.
Aquí está mi constructo basado en este responder y sobre esta GIST:

#include <tuple>
#include <utility>

template<std::size_t N>
struct tuple_functor
{
    template<typename T, typename F>
    static void run(std::size_t i, T&& t, F&& f)
    {
        const std::size_t I = (N - 1);
        switch(i)
        {
        case I:
            std::forward<F>(f)(std::get<I>(std::forward<T>(t)));
            break;

        default:
            tuple_functor<I>::run(i, std::forward<T>(t), std::forward<F>(f));
        }
    }
};

template<>
struct tuple_functor<0>
{
    template<typename T, typename F>
    static void run(std::size_t, T, F){}
};

A continuación, lo utiliza de la siguiente manera:

template<typename... T>
void logger(std::string format, T... args) //behaves like C#'s String.Format()
{
    auto tp = std::forward_as_tuple(args...);
    auto fc = [](const auto& t){std::cout << t;};

    /* ... */

    std::size_t some_index = ...
    tuple_functor<sizeof...(T)>::run(some_index, tp, fc);

    /* ... */
}

No puede haber espacio para mejoras.


De acuerdo con el código de OP, se convertiría en esto:

const std::size_t num = sizeof...(T);
auto my_tuple = std::forward_as_tuple(t...);
auto do_sth = [](const auto& elem){/* ... */};
for(int i = 0; i < num; ++i)
    tuple_functor<num>::run(i, my_tuple, do_sth);

En MSVC STL hay una función _For_each_tuple_element (no documentado):

#include <tuple>

// ...

std::tuple<int, char, float> values{};
std::_For_each_tuple_element(values, [](auto&& value)
{
    // process 'value'
});

De todas las respuestas que he visto aquí, aquí y aquí , me ha gustado < a href = "https://stackoverflow.com/a/6401663/4618482"> @sigidagi 's manera de iterar mejor. Por desgracia, su respuesta es muy detallado 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 y std::array.

template<typename UnaryFunction>
void invoke_with_arg(UnaryFunction)
{}

/**
 * Invoke the unary function with each of the arguments in turn.
 */
template<typename UnaryFunction, typename Arg0, typename... Args>
void invoke_with_arg(UnaryFunction f, Arg0&& a0, Args&&... as)
{
    f(std::forward<Arg0>(a0));
    invoke_with_arg(std::move(f), std::forward<Args>(as)...);
}

template<typename Tuple, typename UnaryFunction, std::size_t... Indices>
void for_each_helper(Tuple&& t, UnaryFunction f, std::index_sequence<Indices...>)
{
    using std::get;
    invoke_with_arg(std::move(f), get<Indices>(std::forward<Tuple>(t))...);
}

/**
 * Invoke the unary function for each of the elements of the tuple.
 */
template<typename Tuple, typename UnaryFunction>
void for_each(Tuple&& t, UnaryFunction f)
{
    using size = std::tuple_size<typename std::remove_reference<Tuple>::type>;
    for_each_helper(
        std::forward<Tuple>(t),
        std::move(f),
        std::make_index_sequence<size::value>()
    );
}

Demostración: coliru

de

C ++ 14 std::make_index_sequence puede implementarse para C ++ 11 .

Uso constexpr y if constexpr (C ++ 17) esto es bastante simple y directo:

template <std::size_t I = 0, typename ... Ts>
void print(std::tuple<Ts...> tup) {
  if constexpr (I == sizeof...(Ts)) {
    return;
  } else {
    std::cout << std::get<I>(tup) << ' ';
    print<I+1>(tup);
  }
}

He tropezado con el mismo problema de la iteración en una tupla de objetos de función, por lo que aquí es una solución más:

#include <tuple> 
#include <iostream>

// Function objects
class A 
{
    public: 
        inline void operator()() const { std::cout << "A\n"; };
};

class B 
{
    public: 
        inline void operator()() const { std::cout << "B\n"; };
};

class C 
{
    public:
        inline void operator()() const { std::cout << "C\n"; };
};

class D 
{
    public:
        inline void operator()() const { std::cout << "D\n"; };
};


// Call iterator using recursion.
template<typename Fobjects, int N = 0> 
struct call_functors 
{
    static void apply(Fobjects const& funcs)
    {
        std::get<N>(funcs)(); 

        // Choose either the stopper or descend further,  
        // depending if N + 1 < size of the tuple. 
        using caller = std::conditional_t
        <
            N + 1 < std::tuple_size_v<Fobjects>,
            call_functors<Fobjects, N + 1>, 
            call_functors<Fobjects, -1>
        >;

        caller::apply(funcs); 
    }
};

// Stopper.
template<typename Fobjects> 
struct call_functors<Fobjects, -1>
{
    static void apply(Fobjects const& funcs)
    {
    }
};

// Call dispatch function.
template<typename Fobjects>
void call(Fobjects const& funcs)
{
    call_functors<Fobjects>::apply(funcs);
};


using namespace std; 

int main()
{
    using Tuple = tuple<A,B,C,D>; 

    Tuple functors = {A{}, B{}, C{}, D{}}; 

    call(functors); 

    return 0; 
}

Salida:

A 
B 
C 
D

Otros han mencionado algunas bibliotecas de terceros bien diseñados que puede recurrir. Sin embargo, si usted está usando C ++ sin esas bibliotecas de terceros, el código siguiente puede ayudar.

namespace detail {

template <class Tuple, std::size_t I, class = void>
struct for_each_in_tuple_helper {
  template <class UnaryFunction>
  static void apply(Tuple&& tp, UnaryFunction& f) {
    f(std::get<I>(std::forward<Tuple>(tp)));
    for_each_in_tuple_helper<Tuple, I + 1u>::apply(std::forward<Tuple>(tp), f);
  }
};

template <class Tuple, std::size_t I>
struct for_each_in_tuple_helper<Tuple, I, typename std::enable_if<
    I == std::tuple_size<typename std::decay<Tuple>::type>::value>::type> {
  template <class UnaryFunction>
  static void apply(Tuple&&, UnaryFunction&) {}
};

}  // namespace detail

template <class Tuple, class UnaryFunction>
UnaryFunction for_each_in_tuple(Tuple&& tp, UnaryFunction f) {
  detail::for_each_in_tuple_helper<Tuple, 0u>
      ::apply(std::forward<Tuple>(tp), f);
  return std::move(f);
}

Nota: El código se compila con cualquier compilador de C ++ supporing 11, y se mantiene la coherencia con el diseño de la biblioteca estándar:

  1. La tupla no tiene por qué ser std::tuple, y en su lugar puede ser cualquier cosa que soporta std::get y std::tuple_size; en particular, std::array y std::pair se pueden utilizar;

  2. La tupla puede ser un tipo de referencia o cv-calificado;

  3. Tiene un comportamiento similar como std::for_each, y devuelve el UnaryFunction de entrada;

  4. Para 14 C ++ (o Laster versión) usuarios, typename std::enable_if<T>::type y typename std::decay<T>::type podría ser reemplazado con su versión simplificada, std::enable_if_t<T> y std::decay_t<T>;

  5. Para 17 C ++ (o Laster versión) usuarios, std::tuple_size<T>::value podría ser reemplazado con su versión simplificada, std::tuple_size_v<T>.

  6. Para 20 C ++ (o versión Laster) usuarios, la característica SFINAE podría ser implementado con el Concepts.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top