get part of std::tuple

2019-01-17 07:13发布

I have a tuple of unknown size (it's template parametr of method)

Is it way to get part of it (I need throw away first element of it)

For example, I have tuple<int,int,int>(7,12,42). I want tuple<int,int>(12,42) here

6条回答
forever°为你锁心
2楼-- · 2019-01-17 07:30

I made some modifications to Adam's code that would strip off the first N arguments of the tuple, as well as create a new tuple with only the last N types ... Here is the complete code (note: if anyone decides to +1 my answer, also please +1 Adam's answer since that is what this code is based on, and I don't wish to take any credit away from his contribution):

//create a struct that allows us to create a new tupe-type with the first
//N types truncated from the front

template<size_t N, typename Tuple_Type>
struct tuple_trunc {};

template<size_t N, typename Head, typename... Tail>
struct tuple_trunc<N, std::tuple<Head, Tail...>>
{
    typedef typename tuple_trunc<N-1, std::tuple<Tail...>>::type type;
};

template<typename Head, typename... Tail>
struct tuple_trunc<0, std::tuple<Head, Tail...>>
{
    typedef std::tuple<Head, Tail...> type;
};

/*-------Begin Adam's Code-----------

Note the code has been slightly modified ... I didn't see the need for the extra
variadic templates in the "assign" structure.  Hopefully this doesn't break something
I didn't forsee

*/

template<size_t N, size_t I>
struct assign 
{
    template<class ResultTuple, class SrcTuple>
    static void x(ResultTuple& t, const SrcTuple& tup) 
    {
        std::get<I - N>(t) = std::get<I>(tup);  
        assign<N, I - 1>::x(t, tup);  //this offsets the assignment index by N
    }
};

template<size_t N>
struct assign<N, 1> 
{
    template<class ResultTuple, class SrcTuple>
    static void x(ResultTuple& t, const SrcTuple& tup) 
    {
        std::get<0>(t) = std::get<1>(tup);
    }
};


template<size_t TruncSize, class Tup> struct th2;

//modifications to this class change "type" to the new truncated tuple type
//as well as modifying the template arguments to assign

template<size_t TruncSize, class Head, class... Tail>
struct th2<TruncSize, std::tuple<Head, Tail...>> 
{
    typedef typename tuple_trunc<TruncSize, std::tuple<Head, Tail...>>::type type;

    static type tail(const std::tuple<Head, Tail...>& tup) 
    {
        type t;
        assign<TruncSize, std::tuple_size<type>::value>::x(t, tup);
        return t;
    }
};

template<size_t TruncSize, class Tup>
typename th2<TruncSize, Tup>::type tail(const Tup& tup) 
{
    return th2<TruncSize, Tup>::tail(tup);
}

//a small example
int main()
{
    std::tuple<double, double, int, double> test(1, 2, 3, 4);
    tuple_trunc<2, std::tuple<double, double, int, double>>::type c = tail<2>(test);
    return 0;
}
查看更多
别忘想泡老子
3楼-- · 2019-01-17 07:35

With C++17, you can use std::apply:

template <typename Head, typename... Tail>
std::tuple<Tail...> tuple_tail(const std::tuple<Head, Tail...>& t)
{
    return apply([](auto head, auto... tail) {
        return std::make_tuple(tail...)};
    }, t);
}
查看更多
姐就是有狂的资本
4楼-- · 2019-01-17 07:40

Please don't use!

  • This is [most probably] unspecified behaviour. It could stop working at any time.
  • Furthermore, there is a possibility of padding issues (i.e. it may work for int but may fail for your type!).

See comments for discussion. I'm leaving this answer just for reference.


Even simpler:

tuple<int,int,int> origin{7,12,42};
tuple<int, int> &tail1 = (tuple<int, int>&)origin;
tuple<int> &tail2 = (tuple<int>&)origin;
cout << "tail1: {" << get<0>(tail1) << ", " << get<1>(tail1) << "}" << endl;
cout << "tail2: {" << get<0>(tail2) << "}" << endl;

I got:

tail1: {12, 42}
tail2: {42}

I'm not certain that this is not an unspecified behaviour. Works for me: Fedora 20 and

❯ clang --version
clang version 3.3 (tags/RELEASE_33/final)
Target: x86_64-redhat-linux-gnu
Thread model: posix
❯ gcc --version
gcc (GCC) 4.8.2 20131212 (Red Hat 4.8.2-7)
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

References: article on voidnish.wordpress.com/.

查看更多
我想做一个坏孩纸
5楼-- · 2019-01-17 07:49

There may be an easier way, but this is a start. The "tail" function template returns a copied tuple with all values of the original except the first. This compiles with GCC 4.6.2 in C++0x-mode.

template<size_t I>
struct assign {
  template<class ResultTuple, class SrcTuple>
  static void x(ResultTuple& t, const SrcTuple& tup) {
    std::get<I - 1>(t) = std::get<I>(tup);
    assign<I - 1>::x(t, tup);
  }
};

template<>
struct assign<1> {
  template<class ResultTuple, class SrcTuple>
  static void x(ResultTuple& t, const SrcTuple& tup) {
    std::get<0>(t) = std::get<1>(tup);
  }
};


template<class Tup> struct tail_helper;

template<class Head, class... Tail>
struct tail_helper<std::tuple<Head, Tail...>> {
  typedef typename std::tuple<Tail...> type;
  static type tail(const std::tuple<Head, Tail...>& tup) {
    type t;
    assign<std::tuple_size<type>::value>::x(t, tup);
    return t;
  }
};

template<class Tup>
typename tail_helper<Tup>::type tail(const Tup& tup) {
  return tail_helper<Tup>::tail(tup);
}
查看更多
Root(大扎)
6楼-- · 2019-01-17 07:52

With help of a compile-time integer list:

#include <cstdlib>

template <size_t... n>
struct ct_integers_list {
    template <size_t m>
    struct push_back
    {
        typedef ct_integers_list<n..., m> type;
    };
};

template <size_t max>
struct ct_iota_1
{
    typedef typename ct_iota_1<max-1>::type::template push_back<max>::type type;
};

template <>
struct ct_iota_1<0>
{
    typedef ct_integers_list<> type;
};

We could construct the tail simply by parameter-pack expansion:

#include <tuple>

template <size_t... indices, typename Tuple>
auto tuple_subset(const Tuple& tpl, ct_integers_list<indices...>)
    -> decltype(std::make_tuple(std::get<indices>(tpl)...))
{
    return std::make_tuple(std::get<indices>(tpl)...);
    // this means:
    //   make_tuple(get<indices[0]>(tpl), get<indices[1]>(tpl), ...)
}

template <typename Head, typename... Tail>
std::tuple<Tail...> tuple_tail(const std::tuple<Head, Tail...>& tpl)
{
    return tuple_subset(tpl, typename ct_iota_1<sizeof...(Tail)>::type());
    // this means:
    //   tuple_subset<1, 2, 3, ..., sizeof...(Tail)-1>(tpl, ..)
}

Usage:

#include <cstdio>

int main()
{
    auto a = std::make_tuple(1, "hello", 7.9);
    auto b = tuple_tail(a);

    const char* s = nullptr;
    double d = 0.0;
    std::tie(s, d) = b;
    printf("%s %g\n", s, d);
    // prints:   hello 7.9

    return 0;
}

(On ideone: http://ideone.com/Tzv7v; the code works in g++ 4.5 to 4.7 and clang++ 3.0)

查看更多
爷、活的狠高调
7楼-- · 2019-01-17 07:52

A tuple slice operation (that also works for std::array and std::pair) can be defined like this (C++14 required):

namespace detail
{
    template <std::size_t Ofst, class Tuple, std::size_t... I>
    constexpr auto slice_impl(Tuple&& t, std::index_sequence<I...>)
    {
        return std::forward_as_tuple(
            std::get<I + Ofst>(std::forward<Tuple>(t))...);
    }
}

template <std::size_t I1, std::size_t I2, class Cont>
constexpr auto tuple_slice(Cont&& t)
{
    static_assert(I2 >= I1, "invalid slice");
    static_assert(std::tuple_size<std::decay_t<Cont>>::value >= I2, 
        "slice index out of bounds");

    return detail::slice_impl<I1>(std::forward<Cont>(t),
        std::make_index_sequence<I2 - I1>{});
}

And an arbitrary subset of a tuple t can be obtained like so :

tuple_slice<I1, I2>(t); 

Where [I1, I2) is the exclusive range of the subset and the return value is a tuple of either references or values depending on whether the input tuple is an lvalue or an rvalue respectively (a thorough elaboration can be found in my blog).

查看更多
登录 后发表回答