Home > front end >  How do I reverse the order of the integers in a `std::integer_sequence<int, 4, -5, 7, -3>`?
How do I reverse the order of the integers in a `std::integer_sequence<int, 4, -5, 7, -3>`?

Time:06-22

Sometimes I want to reverse the values in an index_sequence and use the result to reverse the values in something tuple-like, like in this illustration which reverses the values in a constexpr std::array at compile time.

#include <array>
#include <cstdint>
#include <utility>

namespace detail {
template <class T, std::size_t N, std::size_t... I>
    constexpr std::array<T, N> rev_arr_helper(const std::array<T, N>& arr,
                                              std::index_sequence<I...>) {
        return {arr[sizeof...(I) - I - 1]...};

        //     {arr[4-0-1], arr[4-1-1], arr[4-2-1], arr[4-3-1]}
        //     =>
        //     {arr[3], arr[2], arr[1], arr[0]}
    }
}  // namespace detail

template <class T, std::size_t N>
constexpr std::array<T, N> rev_arr(const std::array<T, N>& arr) {
    return detail::rev_arr_helper(arr, std::make_index_sequence<N>{});
}

int main() {
    constexpr std::array<int, 4> arr{11, 22, 33, 44};
    constexpr auto rev = rev_arr(arr);    
    static_assert(rev[0] == 44 && rev[1] == 33 && rev[2] == 22 && rev[3] == 11, "");
}

Now, this approach does not work for any integer_sequence, like the one in the title. It only works for those ordered 0, 1, 2, ..., N-1 and I would like to be able to make this compile using C 14:

#include <type_traits>
#include <utility>

int main() {
    std::integer_sequence<int, 4, -5, 7, -3> iseq;
    std::integer_sequence<int, -3, 7, -5, 4> itarget;

    auto irev = reverse_sequence(iseq);

    static_assert(std::is_same<decltype(irev), decltype(itarget)>::value, "");
}

How can that be done?


Here's somewhat related Q&A collected from the comments:
How do I reverse the order of element types in a tuple type?

CodePudding user response:

One solution is to create a std::index_sequence to be able to simulate using the subscript operator to access the values in your integer_sequence. By putting the sequence of integers, I..., in a std::tuple one can use std::get<> to extract the value at a certain position in the tuple. We want something similar to I[pos]... (had it worked) which is what we get with:

std::get<pos>( std::tuple<decltype(I)...>{I...} )...
  • Sidenote: <decltype(I)...> above is needed to make it work in C 14. In C 17 and later, one can simply do:
    std::get<pos>( std::tuple{I...} )...
    

Putting that in place makes it very similar to reversing the elements in an array:

#include <cstddef>
#include <tuple>
#include <utility>

namespace detail {
    template <class T, T... I, std::size_t... J>
    constexpr auto rev_helper(std::integer_sequence<T, I...>, std::index_sequence<J...>)
    {
        return
            std::integer_sequence<T,
                std::get<sizeof...(J) - J - 1>(std::tuple<decltype(I)...>{I...})...>{};
//                                  
  • Related