Template tuple - calling a function on each element

后端 未结 7 2064
猫巷女王i
猫巷女王i 2020-11-27 02:28

My question is in the code:

template
struct TupleOfVectors {
  std::tuple...> tuple;

  void do_something_t         


        
相关标签:
7条回答
  • 2020-11-27 03:08

    Here's one approach which may work well in your case:

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

    The only thing that is a bit messy here is the extra dummy int template parameter to do_something_to_each_vec_helper. It is necessary to make the do_something_to_each_vec_helper still be a template when no arguments remain. If you had another template parameter you wanted to use, you could use it there instead.

    0 讨论(0)
  • 2020-11-27 03:13

    If you are not particularly wedded to a solution in the form of generic "for each" function template then you can use one like this:

    #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
    

    A test program, built with GCC 4.7.2 and 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;
    }
    

    The same style of recursion can be used in a generic "for_each" function template without auxiliary indices apparatus:

    #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
    

    And a test program for that:

    #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;
    }
    
    0 讨论(0)
  • 2020-11-27 03:15

    I was testing with tuples and metaprograming and found the current thread. I think my work can inspire someone else although I like the solution of @Andy.

    Anyway, just get fun!

    #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") );
    }
    
    0 讨论(0)
  • 2020-11-27 03:22

    You can quite easily do that with some indices machinery. Given a meta-function gen_seq for generating compile-time integer sequences (encapsulated by the seq class template):

    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...> { };
    }
    

    And the following function templates:

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

    You can use the for_each_in_tuple function above this way:

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

    Here is a live example.

    In your concrete situation, this is how you could use it:

    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...
            }
        };
    };
    

    And once again, here is a live example.

    Update

    If you're using C++14 or later, you can replace the seq and gen_seq classes above with std::integer_sequence like so:

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

    If you're using C++17 or later you can do this (from this comment below):

    std::apply([](auto ...x){std::make_tuple(some_function(x)...);} , the_tuple);
    
    0 讨论(0)
  • 2020-11-27 03:29

    In C++17 you can do this:

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

    given that some_function has suitable overloads for all the types in the tuple.

    This already works in Clang++ 3.9, using std::experimental::apply.

    0 讨论(0)
  • 2020-11-27 03:29

    In addition to the answer of @M. Alaggan, if you need to call a function on tuple elements in order of their appearance in the tuple, in C++17 you can also use a fold expression like this:

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

    (live example).

    Because otherwise order of evaluation of function arguments is unspecified.

    0 讨论(0)
提交回复
热议问题