Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error spliting an std::index_sequence

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?

like image 249
K-ballo Avatar asked Jan 01 '14 23:01

K-ballo


1 Answers

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.

make_index_range

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

slice

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>());
}

split_at

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

Examples of 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>>>(), "");
like image 78
mpark Avatar answered Sep 21 '22 18:09

mpark