Creating a sub-tuple starting from a std::tuple<some_types…>

◇◆丶佛笑我妖孽 提交于 2019-12-23 12:31:23

问题


Let us suppose that a std::tuple<some_types...> is given. I would like to create a new std::tuple whose types are the ones indexed in [0, sizeof...(some_types) - 2]. For instance, let's suppose that the starting tuple is std::tuple<int, double, bool>. I would like to obtain a sub-tuple defined as std::tuple<int, double>.

I'm quite new to variadic templates. As a first step I tried to write a struct in charge of storing the different types of the original std::tuple with the aim of creating a new tuple of the same kind (as in std::tuple<decltype(old_tuple)> new_tuple).

template<typename... types>
struct type_list;

template<typename T, typename... types>
struct type_list<T, types...> : public type_list<types...>  {
    typedef T type;
};


template<typename T>
struct type_list<T> {
    typedef T type;
};

What I would like to do is something like:

std::tuple<type_list<bool, double, int>::type...> new_tuple // this won't work

And the next step would be of discarding the last element in the parameter pack. How can I access the several type's stored in type_list? and how to discard some of them?

Thanks.


回答1:


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.




回答2:


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.




回答3:


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.




回答4:


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>


来源:https://stackoverflow.com/questions/17854219/creating-a-sub-tuple-starting-from-a-stdtuplesome-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!