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<>>);
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.