Home > Back-end >  How to unpack only part of a parameter pack when declaring a variable?
How to unpack only part of a parameter pack when declaring a variable?

Time:04-10

I want to create a tuple that ignores an N number of the first types on a parameter pack, something like this

template<typename... Args>
class Foo
{
 std::tuple</*Args<2>,Args<3>,Args<4> and so on*/> tup;
}

Currently the only solution that I found to achieve something close to this is

template<typename... Args>
    class Foo
    {
      std::vector<std::variant<std::monostate//work for empty parameter pack,Args...>> partargs;
    }

But that makes things a lot harder for me in the future so I was wondering if there was a better solution?

CodePudding user response:

Using class template partial specialization should be enough

#include <tuple>

template<std::size_t N, typename... Args>
struct ignore_first;

template<std::size_t N, typename First, typename... Args>
struct ignore_first<N, First, Args...> : ignore_first<N-1, Args...> { };

template<typename First, typename... Args>
struct ignore_first<0, First, Args...> {
  using type = std::tuple<First, Args...>;
};

template<std::size_t N>
struct ignore_first<N> {
  using type = std::tuple<>;
};

static_assert(std::is_same_v<
  ignore_first<0, int, long, char>::type, std::tuple<int, long, char>>);
static_assert(std::is_same_v<
  ignore_first<1, int, long, char>::type, std::tuple<long, char>>);
static_assert(std::is_same_v<
  ignore_first<2, int, long, char>::type, std::tuple<char>>);
static_assert(std::is_same_v<
  ignore_first<3, int, long, char>::type, std::tuple<>>);

Demo

CodePudding user response:

A variant of the answer of 康桓瑋, using std::conditional and the property of std::tuple_cat to concatenate tuples, also when empty, to avoid recursion.

#include <tuple>
#include <utility>
#include <type_traits>

template <std::size_t N, typename ... Args, std::size_t ... Is>
decltype( std::tuple_cat(
             std::declval<
                std::conditional_t<
                   (N <= Is),
                   std::tuple<Args>,
                   std::tuple<>>>()...) )
   foo (std::index_sequence<Is...>);


template <std::size_t N, typename ... Args>
using ignore_first
   = decltype(foo<N, Args...>(std::index_sequence_for<Args...>{}));

static_assert(std::is_same_v<
  ignore_first<0, int, long, char>, std::tuple<int, long, char>>);
static_assert(std::is_same_v<
  ignore_first<1, int, long, char>, std::tuple<long, char>>);
static_assert(std::is_same_v<
  ignore_first<2, int, long, char>, std::tuple<char>>);
static_assert(std::is_same_v<
  ignore_first<3, int, long, char>, std::tuple<>>);


int main()
{
}

CodePudding user response:

By using the usual index sequence trick, here with shifted indices, and immediately invoked lambdas, one gets a bit shorter than in the accepted answer (and possibly also better in terms of compile time, as it's non-recursive):

#include<tuple>
#include<type_traits>

template<size_t N, typename ... args_t>
constexpr auto skip_first(args_t&& ... args)
{
    return []<size_t ... I>
    (std::index_sequence<I ...>, auto tup)
    {
        return std::tuple{std::get<I N>(std::move(tup)) ...};
    }(std::make_index_sequence<sizeof...(args)-N>{}, std::tuple{std::forward<args_t>(args)...});
}

int main()
{
    static_assert(skip_first<2>(1,2,3,4)==std::tuple{3,4});
}

This requires C 20.

  • Related