I'm trying to split an index_sequence
into two halves. For that, I generate an index_sequence
with the lower half and use it to skip the leading elements on the full index_sequence
. The following is a minimal test case that represents what I'm trying to achieve:
template <int ...I>
struct index_sequence {};
template <int ...I, int ...J>
void foo(index_sequence<I...>, index_sequence<I..., J...>)
{}
int main()
{
foo(index_sequence<0>{}, index_sequence<0, 1>{});
}
I've tried this with the latest versions of Clang, GCC and MSVC, and they all fail to deduce J...
. Is this allowed by the standard? If not, why and what would be a good way to achieve my intent?
If what you want is to split a std::index_sequence
instead of removing the common prefix of two std::index_sequence
s, I think you can benefit from an implementation of slice
and using it to split a std::index_sequence
into pieces.
I'm going to omit the implementation of std::index_sequence
and friends, since you can refer to the paper, N3658, and a sample implementation here.
To implement slice
, we'll use a helper called make_integer_range
. We want a std::index_sequence
generator which gives us [Begin, End) instead of [0, End). Leveraging std::make_integer_sequence
, we get:
template <typename T, typename Seq, T Begin>
struct make_integer_range_impl;
template <typename T, T... Ints, T Begin>
struct make_integer_range_impl<T, std::integer_sequence<T, Ints...>, Begin> {
using type = std::integer_sequence<T, Begin + Ints...>;
};
/* Similar to std::make_integer_sequence<>, except it goes from [Begin, End)
instead of [0, End). */
template <typename T, T Begin, T End>
using make_integer_range = typename make_integer_range_impl<
T, std::make_integer_sequence<T, End - Begin>, Begin>::type;
/* Similar to std::make_index_sequence<>, except it goes from [Begin, End)
instead of [0, End). */
template <std::size_t Begin, std::size_t End>
using make_index_range = make_integer_range<std::size_t, Begin, End>;
Since we don't have a std::get
-like functionality for std::index_sequence
or a variadic template pack, we just build a temporary std::array
to get us std::get
. Then explode the array with only the slice we want.
template <std::size_t... Indices, std::size_t... I>
constexpr decltype(auto) slice_impl(
std::index_sequence<Indices...>,
std::index_sequence<I...>) {
using Array = std::array<std::size_t, sizeof...(Indices)>;
return std::index_sequence<std::get<I>(Array{{Indices...}})...>();
}
template <std::size_t Begin, std::size_t End, std::size_t... Indices>
constexpr decltype(auto) slice(std::index_sequence<Indices...> idx_seq) {
return slice_impl(idx_seq, make_index_range<Begin, End>());
}
One example of using the slice
we just built is to write a split_at
function. We specify the index at which we want to split the std::index_sequence
, and return a pair of std::index_sequence
s split at the given index.
template <std::size_t At, std::size_t... Indices>
constexpr decltype(auto) split_at(index_sequence<Indices...> idx_seq) {
return std::make_pair(slice<0, At>(idx_seq),
slice<At, sizeof...(Indices)>(idx_seq));
}
split_at
:static_assert(std::is_same<
decltype(split_at<2>(index_sequence<1, 4, 2>())),
std::pair<index_sequence<1, 4>, index_sequence<2>>>(), "");
static_assert(std::is_same<
decltype(split_at<1>(index_sequence<1, 4, 2, 3>())),
std::pair<index_sequence<1>, index_sequence<4, 2, 3>>>(), "");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With