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 a somewhat related Q&A collected from the comments:
How do I reverse the order of element types in a tuple type?
A solution that doesn't use std::tuple
is to convert to an std::array
and access the corresponding indices with the help of std::make_index_sequence
.
namespace detail {
template<typename T, T... N, std::size_t... Indices>
constexpr auto reverse_sequence_helper(std::integer_sequence<T, N...> sequence,
std::index_sequence<Indices...>) {
constexpr auto array = std::array<T, sizeof...(N)> { N... };
return std::integer_sequence<T, array[sizeof...(Indices) - Indices - 1]...>();
}
}
template<typename T, T... N>
constexpr auto reverse_sequence(std::integer_sequence<T, N...> sequence) {
return detail::reverse_sequence_helper(sequence,
std::make_index_sequence<sizeof...(N)>());
}
if C++20 is available, it can be reduced to this function with the help of templated lambdas:
template<typename T, T... N>
constexpr auto reverse_sequence(std::integer_sequence<T, N...> sequence) {
constexpr auto array = std::array { N... };
auto make_sequence = [&]<typename I, I... Indices>(std::index_sequence<Indices...>) {
return std::integer_sequence<T, array[sizeof...(Indices) - Indices - 1]...>();
};
return make_sequence(std::make_index_sequence<sizeof...(N)>());
}