Creating a sub-tuple starting from a std::tuple

前端 未结 4 1424
深忆病人
深忆病人 2021-01-18 18:29

Let us suppose that a std::tuple is given. I would like to create a new std::tuple whose types are the ones indexed in [

相关标签:
4条回答
  • 2021-01-18 18:40

    This kind of manipulation is fairly easy with an index sequence technique: generate an index sequence with two fewer indices than your tuple, and use that sequence to select fields from the original. Using std::make_index_sequence and return type deduction from C++14:

    template <typename... T, std::size_t... I>
    auto subtuple_(const std::tuple<T...>& t, std::index_sequence<I...>) {
      return std::make_tuple(std::get<I>(t)...);
    }
    
    template <int Trim, typename... T>
    auto subtuple(const std::tuple<T...>& t) {
      return subtuple_(t, std::make_index_sequence<sizeof...(T) - Trim>());
    }
    

    In C++11:

    #include <cstddef>     // for std::size_t
    
    template<typename T, T... I>
    struct integer_sequence {
      using value_type = T;
    
      static constexpr std::size_t size() noexcept {
        return sizeof...(I);
      }
    };
    
    namespace integer_sequence_detail {
    template <typename, typename> struct concat;
    
    template <typename T, T... A, T... B>
    struct concat<integer_sequence<T, A...>, integer_sequence<T, B...>> {
      typedef integer_sequence<T, A..., B...> type;
    };
    
    template <typename T, int First, int Count>
    struct build_helper {
      using type = typename concat<
        typename build_helper<T, First,           Count/2>::type,
        typename build_helper<T, First + Count/2, Count - Count/2>::type
      >::type;
    };
    
    template <typename T, int First>
    struct build_helper<T, First, 1> {
      using type = integer_sequence<T, T(First)>;
    };
    
    template <typename T, int First>
    struct build_helper<T, First, 0> {
      using type = integer_sequence<T>;
    };
    
    template <typename T, T N>
    using builder = typename build_helper<T, 0, N>::type;
    } // namespace integer_sequence_detail
    
    template <typename T, T N>
    using make_integer_sequence = integer_sequence_detail::builder<T, N>;
    
    template <std::size_t... I>
    using index_sequence = integer_sequence<std::size_t, I...>;
    
    template<size_t N>
    using make_index_sequence = make_integer_sequence<size_t, N>;
    
    #include <tuple>
    
    template <typename... T, std::size_t... I>
    auto subtuple_(const std::tuple<T...>& t, index_sequence<I...>) 
      -> decltype(std::make_tuple(std::get<I>(t)...))
    {
      return std::make_tuple(std::get<I>(t)...);
    }
    
    template <int Trim, typename... T>
    auto subtuple(const std::tuple<T...>& t)
      -> decltype(subtuple_(t, make_index_sequence<sizeof...(T) - Trim>()))
    {
      return subtuple_(t, make_index_sequence<sizeof...(T) - Trim>());
    }
    

    Live at Coliru.

    0 讨论(0)
  • 2021-01-18 18:47

    One way to do it is to recursively pass two tuples to a helper struct that takes the first element of the "source" tuple and adds it to the end of the another one:

    #include <iostream>
    #include <tuple>
    #include <type_traits>
    
    namespace detail {
    
        template<typename...>
        struct truncate;
    
        // this specialization does the majority of the work
    
        template<typename... Head, typename T, typename... Tail>
        struct truncate< std::tuple<Head...>, std::tuple<T, Tail...> > {
            typedef typename
            truncate< std::tuple<Head..., T>, std::tuple<Tail...> >::type type;
        };
    
        // this one stops the recursion when there's only
        // one element left in the source tuple
    
        template<typename... Head, typename T>
        struct truncate< std::tuple<Head...>, std::tuple<T> > {
            typedef std::tuple<Head...> type;
        };
    }
    
    template<typename...>
    struct tuple_truncate;
    
    template<typename... Args>
    struct tuple_truncate<std::tuple<Args...>> {
    
        // initiate the recursion - we start with an empty tuple,
        // with the source tuple on the right
    
        typedef typename detail::truncate< std::tuple<>, std::tuple<Args...> >::type type;
    };
    
    int main()
    {
        typedef typename tuple_truncate< std::tuple<bool, double, int> >::type X;
    
        // test
        std::cout << std::is_same<X, std::tuple<bool, double>>::value; // 1, yay
    }
    

    Live example.

    0 讨论(0)
  • 2021-01-18 18:55

    Here is a way to solve your problem directly.

    template<unsigned...s> struct seq { typedef seq<s...> type; };
    template<unsigned max, unsigned... s> struct make_seq:make_seq<max-1, max-1, s...> {};
    template<unsigned...s> struct make_seq<0, s...>:seq<s...> {};
    
    template<unsigned... s, typename Tuple>
    auto extract_tuple( seq<s...>, Tuple& tup ) {
      return std::make_tuple( std::get<s>(tup)... );
    }
    

    You can use this as follows:

    std::tuple< int, double, bool > my_tup;
    auto short_tup = extract_tuple( make_seq<2>(), my_tup );
    auto skip_2nd = extract_tuple( seq<0,2>(), my_tup );
    

    and use decltype if you need the resulting type.

    A completely other approach would be to write append_type, which takes a type and a tuple<...>, and adds that type to the end. Then add to type_list:

    template<template<typename...>class target>
    struct gather {
      typedef typename type_list<types...>::template gather<target>::type parent_result;
      typedef typename append< parent_result, T >::type type;
    };
    

    which gives you a way to accumulate the types of your type_list into an arbitrary parameter pack holding template. But that isn't required for your problem.

    0 讨论(0)
  • 2021-01-18 19:00

    Subrange from tuple with boundary checking, without declaring "helper classes":

    template <size_t starting, size_t elems, class tuple, class seq = decltype(std::make_index_sequence<elems>())>
    struct sub_range;
    
    template <size_t starting, size_t elems, class ... args, size_t ... indx>
    struct sub_range<starting, elems, std::tuple<args...>, std::index_sequence<indx...>>
    {
        static_assert(elems <= sizeof...(args) - starting, "sub range is out of bounds!");
        using tuple = std::tuple<std::tuple_element_t<indx + starting, std::tuple<args...>> ...>;
    };
    

    Usage:

    struct a0;
    ...
    struct a8;
    
    using range_outer = std::tuple<a0, a1, a2, a3, a4, a5, a6, a7, a8>;
    sub_range<2, 3, range_outer>::tuple; //std::tuple<a2, a3, a4>
    
    0 讨论(0)
提交回复
热议问题