Is it possible to turn a constexpr std::array into a std::integer_sequence?

Viewed 636

That is, given a constexpr std::array<int,2>{1,2} pass it to some function or helper class that would spit out the type std::integer_sequence<int, 1, 2>?

It seems easy to jump from the type world to the "constexpr value" world (e.g., to do the reverse conversion), but hard or impossible to do the reverse.

3 Answers

It seems like you can do it in C++17, at the cost of introducing a lambda at the call site:

template <size_t N, typename F, size_t... indexes>
constexpr auto make_seq_helper(F f, std::index_sequence<indexes...> is) {
    return std::integer_sequence<int, std::get<indexes>(f())...>{};
}

template <typename F>
constexpr auto make_seq(F f) {
    constexpr size_t N = f().size();
    using indexes = std::make_index_sequence<N>;
    return make_seq_helper<N>(f, indexes{});
};

Calling make_seq like this:

    constexpr std::array a{7, 15, 28};
    auto x = make_seq([](){ return a; });

Results in an x with type std::integer_sequence<int, 7, 15, 28>. I'm not sure if the lambda use can be removed.

If array have external linkage, you might do something like:

template <auto& Arr, size_t... Is>
constexpr auto make_seq_impl(std::index_sequence<Is...>) {
    using T = typename std::decay_t<decltype(Arr)>::value_type;
    return std::integer_sequence<T, Arr[Is]...>{};
}

template <auto& Arr>
constexpr auto make_seq() {
    return make_seq_impl<Arr>(std::make_index_sequence<Arr.size()>());
}

constexpr std::array a{7, 15, 28};

int main()
{
    [[maybe_unused]]auto x = make_seq<a>();
    static_assert(std::is_same<std::integer_sequence<int, 7, 15, 28>, decltype(x)>::value, "!");
}

Demo

Or, with structure way, you might do:

template <const auto& Arr, typename Seq = std::make_index_sequence<std::size(Arr)>>
struct make_seq;

template <typename T, std::size_t N, const std::array<T, N>& Arr, std::size_t ... Is>
struct make_seq<Arr, std::index_sequence<Is...>>
{
    using type = std::integer_sequence<T, Arr[Is]...>;
};

Demo

Here's a C++14 compatible solution.

A trick to "pass" a constexpr std::array (or any other struct/class object) as a template argument is to wrap it into a type:

constexpr std::array<int,3> a{7,15,28};
struct ArrayWrapper_a {
    static constexpr auto& value = a;
};

template<typename ArrayWrapper>
struct Foobar {
    // do stuff with ArrayWrapper::value
}

Then you can do something similar to BeeOnRope's answer to generate sequences:

template<typename ArrayWrapper, typename Sequence>
struct array_to_sequence_impl;

template<typename ArrayWrapper, std::size_t... indices>
struct array_to_sequence_impl<ArrayWrapper,std::index_sequence<indices...>> {
    using value_type = typename std::decay_t<decltype(ArrayWrapper::value)>::value_type;
    using type = std::integer_sequence<value_type, std::get<indices>(ArrayWrapper::value)...>;
};

template<typename ArrayWrapper>
using array_to_sequence = typename array_to_sequence_impl<ArrayWrapper,std::make_index_sequence<ArrayWrapper::value.size()>>::type;

Usage:

constexpr std::array<int,3> a{7,15,28};
struct ArrayWrapper_a {
    static constexpr auto& value = a;
};
using Sequence_a = array_to_sequence<ArrayWrapper_a>;

Live demo

Related