C++ Logo

std-proposals

Advanced search

[std-proposals] Fwd: destructurable std::integer_sequence

From: Desmond Gold <desmondbongcawel922_at_[hidden]>
Date: Sat, 30 Nov 2024 22:15:00 +0800
---------- Forwarded message ---------
From: Desmond Gold <desmondbongcawel922_at_[hidden]>
Date: Sat, Nov 30, 2024 at 8:15 PM
Subject: destructurable std::integer_sequence
To: <std-proposals_at_[hidden]>


After P1061R10 <https://wg21.link/P1061R10> has been approved for C++26,
it's now time to let std::integer_sequence be a destructurable type for
improved usability while still remain its niche

auto [...Is] = std::make_index_sequence<N>();

This addition helps simplify metaprogramming implementations that rely on
std::integer_sequence. Most of these implementations declare another helper
function that takes std::integer_sequence that does the actual work. But
with this addition, you don't need to do that.

// before:
template <typename Array, std::size_t... I>
constexpr auto array_to_tuple_impl(const Array& a,
std::index_sequence<I...>)
{
    return std::make_tuple(a[I]...);
}

template <typename T, std::size_t N, typename Indx =
std::make_index_sequence<N>>
constexpr auto array_to_tuple(const std::array<T, N>& a)
{
    return details::array_to_tuple_impl(a, Indx{});
}

// after:
template <typename T, std::size_t N>
constexpr auto array_to_tuple(const std::array<T, N>& a)
{
    auto [...Is] = std::make_index_sequence<N>();
    return std::make_tuple(a[Is]...);
}

One of the motivating examples of P1061R10 which requires
std::index_sequence to be destructurable:
template <class P, class Q>
auto dot_product(P p, Q q) {
    // no helper function necessary!
    auto [...Is] = std::make_index_sequence<std::tuple_size_v<P>>{};
    return (... + (std::get<Is>(p) * std::get<Is>(q)));
}

There are two options in making these destructurable sequences possible:
1.) destructured into individual std::integral_constant:

  template<typename _Tp, _Tp... _Is>
    struct tuple_size<std::integer_sequence<_Tp, _Is...>>
    : std::integral_constant<std::size_t, sizeof...(_Is)> { };

  template<std::size_t _I, typename _Tp, _Tp... _Is>
    struct tuple_element<_I, std::integer_sequence<_Tp, _Is...>>
    { using type = std::integral_constant<_Tp, _Is...[_I]>; };

  template<std::size_t _I, typename _Tp, _Tp... _Is>
    constexpr std::integral_constant<_Tp, _Is...[_I]>
    get(std::integer_sequence<_Tp, _Is...>) noexcept
    { return {}; }

2.) destructured into constant integers:

  template<typename _Tp, _Tp... _Is>
    struct tuple_size<std::integer_sequence<_Tp, _Is...>>
    : std::integral_constant<std::size_t, sizeof...(_Is)> { };

  template<std::size_t _I, typename _Tp, _Tp... _Is>
    struct tuple_element<_I, std::integer_sequence<_Tp, _Is...>>
    { using type = _Tp; };

  template<std::size_t _I, typename _Tp, _Tp... _Is>
    constexpr _Tp get(std::integer_sequence<_Tp, _Is...>) noexcept
    { return _Is...[_I]; }

There are notable differences between these options.
- Option 1 decomposes into std::integral_constant of different types. This
allows you to declare like this which is usable in constant-expressions
converted into underlying value type.

auto [...Is] = std::make_index_sequence<N>();

- Option 2 decomposes into integral constants of the same value type. This
requires you to declare structured binding as constexpr (made possible by
P2686R5 <https://wg21.link/P2686R5>).

constexpr auto [...Is] = std::make_index_sequence<N>();

However, there is one problem. What if the integer sequence is empty? I
don't know if P1061R10 permits empty structured binding packs from an
object of zero tuple size. If this isn't currently allowed, then I believe
this is the hole of the proposal which needs a core language fix
(probably?) or worse never add this library feature because of this. Or add
this and make workarounds such as:

template <typename T, std::size_t N>
constexpr auto array_to_tuple(const std::array<T, N>& a)
{
    if constexpr (N != 0)
    {
        auto [...Is] = std::make_index_sequence<N>();
        return std::make_tuple(a[Is]...);
    }
    else
    {
        return std::make_tuple();
    }
}

Received on 2024-11-30 14:16:18