声明变量时如何只解包参数包的一部分?

How to unpack only part of a parameter pack when declaring a variable?

我想创建一个元组来忽略参数包中第一个类型的 N 个,像这样

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

目前我找到的唯一接近于此的解决方案是

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

但这让我以后的事情变得更加困难,所以我想知道是否有更好的解决方案?

使用 class 模板部分专业化应该足够了

#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

康桐蔚答案的一个变体,使用 std::conditionalstd::tuple_cat 的 属性 连接元组,在空的时候也是如此,以避免递归。

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

通过使用通常的索引序列技巧,这里使用移位索引,并立即调用 lambdas,一个比接受的答案短一点(并且在编译时间方面也可能更好,因为它是 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});
}

这需要 C++20。在 C++17 中,您必须定义两个单独的函数:

template<size_t N, size_t ... I, typename tuple_t>
constexpr auto skip_first(std::index_sequence<I ...>, tuple_t tup)
{
    return std::tuple{std::get<I+N>(std::move(tup)) ...};
}

template<size_t N, typename ... args_t>
constexpr auto skip_first(args_t&& ... args)
{
    return skip_first<N>(std::make_index_sequence<sizeof...(args)-N>{}, std::tuple{std::forward<args_t>(args)...});
}