Mi pregunta es en el código:

template<typename... Ts>
struct TupleOfVectors {
  std::tuple<std::vector<Ts>...> tuple;

  void do_something_to_each_vec() {
    //Question: I want to do this:
    // "for each (N)": do_something_to_vec<N>()
    //How?
  }

  template<size_t N>
  void do_something_to_vec() {
    auto &vec = std::get<N>(tuple);
    //do something to vec
  }
};
InformationsquelleAutor 7cows | 2013-05-05

6 Comentarios

  1. 34

    Usted puede hacerlo fácilmente con algunos índices de la maquinaria. Dado un meta-función gen_seq para generar en tiempo de compilación secuencias de enteros (encapsulado por el seq plantilla de clase):

    namespace detail
    {
        template<int... Is>
        struct seq { };
    
        template<int N, int... Is>
        struct gen_seq : gen_seq<N - 1, N - 1, Is...> { };
    
        template<int... Is>
        struct gen_seq<0, Is...> : seq<Is...> { };
    }

    Y la siguiente función plantillas:

    #include <tuple>
    
    namespace detail
    {
        template<typename T, typename F, int... Is>
        void for_each(T&& t, F f, seq<Is...>)
        {
            auto l = { (f(std::get<Is>(t)), 0)... };
        }
    }
    
    template<typename... Ts, typename F>
    void for_each_in_tuple(std::tuple<Ts...> const& t, F f)
    {
        detail::for_each(t, f, detail::gen_seq<sizeof...(Ts)>());
    }

    Puede utilizar el for_each_in_tuple función anterior de la siguiente forma:

    #include <string>
    #include <iostream>
    
    struct my_functor
    {
        template<typename T>
        void operator () (T&& t)
        {
            std::cout << t << std::endl;
        }
    };
    
    int main()
    {
        std::tuple<int, double, std::string> t(42, 3.14, "Hello World!");
        for_each_in_tuple(t, my_functor());
    }

    Aquí es un ejemplo.

    En su situación concreta, esta es la forma en que se podría utilizar:

    template<typename... Ts>
    struct TupleOfVectors
    {
        std::tuple<std::vector<Ts>...> t;
    
        void do_something_to_each_vec()
        {
            for_each_in_tuple(t, tuple_vector_functor());
        }
    
        struct tuple_vector_functor
        {
            template<typename T>
            void operator () (T const &v)
            {
                //Do something on the argument vector...
            }
        };
    };

    Y una vez más, aquí es un ejemplo.

    • metaprogramación es comer mi cerebro. no estoy seguro donde las cosas empiezan, lo que está llamando qué, y qué/dónde es el resultado final. no como la programación regular a todos. Tomará algún tiempo para descifrar esto 🙂
    • Por supuesto. En el principio no es fácil en absoluto, y tal vez este no es exactamente el ejemplo lo más fácil para empezar, pero estoy seguro que con un poco de práctica pronto comprenderlo 😉
    • void for_each(T&& t, F f, seq<Is...>) : ¿Por qué el último argumento no tiene un identificador?
    • Porque no se utiliza dentro de la función
    • ¿Cuál es su propósito?
    • Se permite la deducción Is...
    • Sí:) ¿por Qué la auto l variable que no se usa, y por qué (en la misma línea) el extra 0 argumento?
    • El auto l es permitir el tratamiento de lo que viene en el lado derecho como una lista de inicializador, lo que garantiza la ampliación de las expresiones se evalúan en el orden correcto. El (f(x), 0) utiliza el operador coma, de modo que f(x) se evalúa, pero el valor resultante se descarta, y el valor de la expresión (f(x), 0) es 0. Esto es para darle todos los elementos de la lista de inicializador del mismo tipo (int, aquí), con el fin de hacer posible deducir el tipo de la lista de inicializador (initializer_list<int>)
    • Si su compilador soporta, intenta void do_in_order() {} template<typename F0, typename Fs...> void do_in_order( F0&& f0, Fs&&... fs ) { std::forward<F0>(f0)(); do_in_order(std::forward<FS>(fs)...); }, que se llama como do_in_order([&]{f(std::get<Is>(t));}...); — crear un variardic conjunto de lambdas el cual se invocan los do_in_order. Por encima y más allá del problema que el anterior hack tiene agujeros en ella (si f devuelve un tipo que anula operator,, el resultado es inesperado), esta mejor describe lo que está haciendo. Tristemente, muchos compiladores falta el C++11 el apoyo necesario para este…
    • Muchos años más tarde, podría la primera parte de ser sustituido por std::integer_sequence?

  2. 28

    En C++17 usted puede hacer esto:

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

    dado que some_function ha adecuado sobrecargas para todos los tipos de la tupla.

    Este ya funciona en Clang++ 3.9, utilizando std::experimental::apply.

    • No esta conducen a la iteración – es decir, llamadas de do_something() – ocurren en un orden no definido, ya que el parámetro de paquete se expande dentro de una llamada a una función (), en el que los argumentos tienen no especificado hacer el pedido? Que podría ser muy importante; me imagino que la mayoría de la gente espera de la orden a garantizar que se producen en el mismo orden que los miembros, es decir, como los índices de std::get<>(). AFAIK, para obtener un orden garantizado en casos como este, la expansión debe ser hecho dentro de los {braces}. Estoy equivocado? Esta respuesta pone énfasis en el pedido: stackoverflow.com/a/16387374/2757035
    • C++17 garantiza la ejecución de la orden de los argumentos de la función. Desde esta respuesta se aplica a la de C++17, esto es válido.
    • Soy consciente de que algunos cambios a la orden de evaluación/garantías en determinados contextos, pero no en los argumentos a funciones, a excepción de algunos de ida y vuelta donde de izquierda a derecha pedido era considera, pero rechazó. Buscar en el actual borrador de la Norma: github.com/cplusplus/draft/blob/master/source/… The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter.
    • Estoy de acuerdo en que esto va a indefinido iteración orden, incluso con C++17.
    • std::apply([](auto&& ...x){ (static_cast<void>(some_function(std::forward<decltype(x)>(x))), ...);} , the_tuple); garantizar el orden de evaluación, y permite some_function para volver void (e incluso el mal clases con sobrecargado operator ,).
    • ya que hablamos de c++17 aquí, un pliegue de expresión como std::apply([](auto& ...x){(..., some_function(x));}, the_tuple); es mejor stackoverflow.com/a/45498003/8414561
    • La mía también se utiliza a veces la expresión, sino que usa el argumento adelante y asas de clase con el mal, con la sobrecarga de coma. Triste que la simplicidad tiene algo de trampa :-/
    • sí, no se dio cuenta de que, mi mal; es por eso que he decidido no usar forward para simplificar la lectura 🙂 por favor podría dar un ejemplo de «mal de sobrecarga de coma» y los problemas que puede causar?
    • href=»http://coliru.stacked-crooked.com/a/d731bb5ce28a4b76″ >Demo de mal operador coma en acción.

  3. 7

    Este es un enfoque que puede funcionar bien en su caso:

    template<typename... Ts>
    struct TupleOfVectors {
        std::tuple<std::vector<Ts>...> tuple;
    
        void do_something_to_each_vec()
        {
            //First template parameter is just a dummy.
            do_something_to_each_vec_helper<0,Ts...>();
        }
    
        template<size_t N>
        void do_something_to_vec()
        {
            auto &vec = std::get<N>(tuple);
            //do something to vec
        }
    
    private:
        //Anchor for the recursion
        template <int>
        void do_something_to_each_vec_helper() { }
    
        //Execute the function for each template argument.
        template <int,typename Arg,typename...Args>
        void do_something_to_each_vec_helper()
        {
            do_something_to_each_vec_helper<0,Args...>();
            do_something_to_vec<sizeof...(Args)>();
        }
    };

    La única cosa que es un poco desordenado aquí está el extra ficticio int parámetro de plantilla para do_something_to_each_vec_helper. Es necesario hacer la do_something_to_each_vec_helper todavía una plantilla cuando no hay argumentos siguen siendo. Si usted tuvo otro parámetro de plantilla que quería usar, usted puede usarlo hay lugar.

    • Es bastante brillante de cómo este se las arregla para llamar la do_something_to_vec método correcto 0,1,2,… orden ¿no? Me gusta… 🙂
    • template<typename Arg> void do_something_to_each_vec_helper() { do_something_to_vec<)(); template<typename Arg, typename...Args> void do_something_to_each_vec_helper() { do_something_to_each_vec_helper<Args...>(); do_something_to_vec<sizeof...(Args)>(); } se deshace de que las int, pero no se cumple DRY (don’t repeat yourself) en algún grado. Hmm.
    • Me sale un error de ambigüedad si puedo probar eso.
    • de verdad? Pensé que en el caso de la ambigüedad, el uno sin el parámetro pack fue uno de los elegidos. Supongo que estaba equivocado, tal vez de que sólo se aplica cuando se trata de un variardic conjunto de argumentos, y no los tipos puros?
  4. 6

    Si usted no está particularmente ligado a una solución en forma de genéricos
    «para cada» función de la plantilla, a continuación, puede utilizar uno como este:

    #ifndef TUPLE_OF_VECTORS_H
    #define TUPLE_OF_VECTORS_H
    
    #include <vector>
    #include <tuple>
    #include <iostream>
    
    template<typename... Ts>
    struct TupleOfVectors 
    {
        std::tuple<std::vector<Ts>...> tuple;
    
        template<typename ...Args>
        TupleOfVectors(Args... args)
        : tuple(args...){}
    
        void do_something_to_each_vec() {
            do_something_to_vec(tuple);
        }
    
        template<size_t I = 0, class ...P>
        typename std::enable_if<I == sizeof...(P)>::type
        do_something_to_vec(std::tuple<P...> &) {}
    
        template<size_t I = 0, class ...P>
        typename std::enable_if<I < sizeof...(P)>::type
        do_something_to_vec(std::tuple<P...> & parts) {
            auto & part = std::get<I>(tuple);
            //Doing something...
            std::cout << "vector[" << I << "][0] = " << part[0] << std::endl;
            do_something_to_vec<I + 1>(parts);
        }
    };
    
    #endif //EOF

    Un programa de prueba, construido con GCC 4.7.2 y clang 3.2:

    #include "tuple_of_vectors.h"
    
    using namespace std;
    
    int main()
    {
        TupleOfVectors<int,int,int,int> vecs(vector<int>(1,1),
            vector<int>(2,2),
            vector<int>(3,3),
            vector<int>(4,4));
    
        vecs.do_something_to_each_vec();
        return 0;
    }

    El mismo estilo de la recursividad puede ser utilizado en un genérico «for_each»
    plantilla de función sin auxiliar índices de aparato:

    #ifndef FOR_EACH_IN_TUPLE_H
    #define FOR_EACH_IN_TUPLE_H
    
    #include <type_traits>
    #include <tuple>
    #include <cstddef>
    
    template<size_t I = 0, typename Func, typename ...Ts>
    typename std::enable_if<I == sizeof...(Ts)>::type
    for_each_in_tuple(std::tuple<Ts...> &, Func) {}
    
    template<size_t I = 0, typename Func, typename ...Ts>
    typename std::enable_if<I < sizeof...(Ts)>::type
    for_each_in_tuple(std::tuple<Ts...> & tpl, Func func) 
    {
        func(std::get<I>(tpl));
        for_each_in_tuple<I + 1>(tpl,func);
    }
    
    #endif //EOF

    Y un programa de prueba para que:

    #include "for_each_in_tuple.h"
    #include <iostream>
    
    struct functor
    {
        template<typename T>
        void operator () (T&& t)
        {
            std::cout << t << std::endl;
        }
    };
    
    int main()
    {
        auto tpl = std::make_tuple(1,2.0,"Three");
        for_each_in_tuple(tpl,functor());
        return 0;
    }
    • esta respuesta fue mucho más útil para mí, y no generar alertas como algunos de los otros. +1
  5. 2

    Yo estaba probando con tuplas y metaprograming y encuentra el subproceso actual.
    Creo que mi trabajo pueda inspirar a alguien más, aunque me gusta la solución de @Andy.

    De todos modos, acaba de conseguir la diversión!

    #include <tuple>
    #include <type_traits>
    #include <iostream>
    #include <sstream>
    #include <functional>
    template<std::size_t I = 0, typename Tuple, typename Func>
    typename std::enable_if< I != std::tuple_size<Tuple>::value, void >::type
    for_each(const Tuple& tuple, Func&& func)
    {
    func(std::get<I>(tuple));
    for_each<I + 1>(tuple, func);
    }
    template<std::size_t I = 0, typename Tuple, typename Func>
    typename std::enable_if< I == std::tuple_size<Tuple>::value, void >::type
    for_each(const Tuple& tuple, Func&& func)
    {
    //do nothing
    }
    struct print
    {
    template<typename T>
    void operator () (T&& t)
    {
    std::cout << t << std::endl;
    }
    };
    template<typename... Params>
    void test(Params&& ... params)
    {
    int sz = sizeof...(params);
    std::tuple<Params...> values(std::forward<Params>(params)...);
    for_each(values, print() );
    }
    class MyClass
    {
    public:
    MyClass(const std::string& text) 
    : m_text(text)
    {
    }
    friend std::ostream& operator <<(std::ostream& stream, const MyClass& myClass)
    {
    stream << myClass.m_text;
    return stream;
    }
    private:
    std::string m_text;
    };
    int main()
    {
    test(1, "hello", 3.f, 4, MyClass("I don't care") );
    }
    • Gran pieza de código! Sin embargo, tal como está no funciona en línea con las lambdas como se espera de un l-valor func. La función de la firma debe ser cambiado a for_each(const Tuple& tuple, Func&& func), con un Func&& func argumento para permitir el paso de un temporal de lambda.
    • Estás en lo cierto, pues cuenta 😀

Dejar respuesta

Please enter your comment!
Please enter your name here