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_sequences, 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_sequences 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