[PATCH] libstdc++: Implement P1789R3: structured bindings for std::integer_sequence
Matthias Wippich
mfwippich@gmail.com
Sun Apr 5 18:33:19 GMT 2026
>Will post an updated patch, but wouldn’t pre-CWG3135 wording also work, due to lifetime extension?
Without the changes from cwg3135, this cannot interact well with p2686
constexpr structured bindings. The non-constexpr case should be fine.
That's very unfortunate since the intended use is with p2686 constexpr
bindings + p1061 pack-introducing bindings - therefore, implementing
p1789 without first addressing cwg3135 seems moot to me.
As Tomasz mentioned, I already submitted a patch for this end of last
year but waited to see whether we fix p1789 by addressing the core
language defect or by changing the library specification instead.
Since cwg3135 is accepted now, I was going to update it as soon as
Jakub's patch for cwg3135 from 2 days ago is merged (so tests pass).
Since your patch seems to be in better shape than mine, I do not mind
abandoning the previous patch in favor of this one. @Tomasz: Which one
do you want to go ahead with? Does the release target change depending
on which one is used or is it going to be GCC 17 either way?
On Sun, Apr 5, 2026 at 1:38 PM Ivan Lazaric <ivan.lazaric1@gmail.com> wrote:
>
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p1789r3.pdf
>
> P1789 enables accessing the sequence values through structured bindings.
> ```
> auto [...values] = std::make_index_sequence<10>{};
> // values is a pack of size 10, and elements 0, 1, 2, ..., 9
> ```
>
> Corresponding C++ draft commit: 3d71a838ed2a1689dd329f964ec4d58152487151
> Feature-test macro integer_sequence has been bumped to 202511L.
>
> libstdc++-v3/ChangeLog:
>
> * include/bits/utility.h:
> Implement structured bindings protocol for std::integer_sequence.
> * include/bits/version.def: Bump integer_sequence feature-test macro.
> * include/bits/version.h: Regenerate.
> * testsuite/20_util/integer_sequence/structured_binding.cc: New test.
>
> Signed-off-by: Ivan Lazaric <ivan.lazaric1@gmail.com>
> ---
> Fixed a missing `std::` on `size_t` , thanks Linaro.
> Extended the test.
>
> Only part that fails is the last block in `test_basic()`
> I am pretty sure that's because of:
> ```
> static constexpr int&& x = 1;
> static_assert(x == 1); // fails, would've worked if it was `const int&&` or `const int&`
> ```
>
> https://godbolt.org/z/jM18sv5nh
>
> libstdc++-v3/include/bits/utility.h | 28 ++++++++
> libstdc++-v3/include/bits/version.def | 5 ++
> libstdc++-v3/include/bits/version.h | 7 +-
> .../integer_sequence/structured_binding.cc | 72 +++++++++++++++++++
> 4 files changed, 111 insertions(+), 1 deletion(-)
> create mode 100644 libstdc++-v3/testsuite/20_util/integer_sequence/structured_binding.cc
>
> diff --git a/libstdc++-v3/include/bits/utility.h b/libstdc++-v3/include/bits/utility.h
> index bd6b18d54dd..159884dd814 100644
> --- a/libstdc++-v3/include/bits/utility.h
> +++ b/libstdc++-v3/include/bits/utility.h
> @@ -150,6 +150,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> static constexpr size_t size() noexcept { return sizeof...(_Idx); }
> };
>
> +#if __glibcxx_integer_sequence >= 202511L // C++ >= 26
> + template<typename _Tp, _Tp... _Idx>
> + struct tuple_size<integer_sequence<_Tp, _Idx...>>
> + : integral_constant<size_t, sizeof...(_Idx)> { };
> +
> + template<size_t __i, class _Tp, _Tp... _Idx>
> + struct tuple_element<__i, integer_sequence<_Tp, _Idx...>>
> + {
> + static_assert(__i < sizeof...(_Idx));
> + using type = _Tp;
> + };
> +
> + template<size_t __i, class _Tp, _Tp... _Idx>
> + struct tuple_element<__i, const integer_sequence<_Tp, _Idx...>>
> + {
> + static_assert(__i < sizeof...(_Idx));
> + using type = _Tp;
> + };
> +
> + template<size_t __i, class _Tp, _Tp... _Idx>
> + constexpr _Tp
> + get (integer_sequence<_Tp, _Idx...>) noexcept
> + {
> + static_assert(__i < sizeof...(_Idx));
> + return _Idx...[__i];
> + }
> +#endif // __glibcxx_integer_sequence >= 202511L
> +
> /// Alias template make_integer_sequence
> template<typename _Tp, _Tp _Num>
> using make_integer_sequence
> diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
> index 1265f01757c..81ec5723844 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -184,6 +184,11 @@ ftms = {
>
> ftms = {
> name = integer_sequence;
> + values = {
> + v = 202511;
> + cxxmin = 26;
> + extra_cond = "__cpp_pack_indexing";
> + };
> values = {
> v = 201304;
> cxxmin = 14;
> diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
> index 00f352089f7..cde4fa839db 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -186,7 +186,12 @@
> #undef __glibcxx_want_exchange_function
>
> #if !defined(__cpp_lib_integer_sequence)
> -# if (__cplusplus >= 201402L)
> +# if (__cplusplus > 202302L) && (__cpp_pack_indexing)
> +# define __glibcxx_integer_sequence 202511L
> +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_integer_sequence)
> +# define __cpp_lib_integer_sequence 202511L
> +# endif
> +# elif (__cplusplus >= 201402L)
> # define __glibcxx_integer_sequence 201304L
> # if defined(__glibcxx_want_all) || defined(__glibcxx_want_integer_sequence)
> # define __cpp_lib_integer_sequence 201304L
> diff --git a/libstdc++-v3/testsuite/20_util/integer_sequence/structured_binding.cc b/libstdc++-v3/testsuite/20_util/integer_sequence/structured_binding.cc
> new file mode 100644
> index 00000000000..18cbcae53e5
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/integer_sequence/structured_binding.cc
> @@ -0,0 +1,72 @@
> +// { dg-do compile { target c++26 } }
> +
> +#include <utility>
> +#include <testsuite_hooks.h>
> +
> +#if __cpp_lib_integer_sequence < 202511L
> +# error "Feature-test macro __cpp_lib_integer_sequence is incorrect"
> +#endif
> +
> +constexpr auto
> +destructure_sum(auto seq)
> +{
> + auto [...elems] = seq;
> + return (0 + ... + elems);
> +}
> +
> +using IS1 = std::make_index_sequence<10>;
> +static_assert( std::tuple_size_v<IS1> == 10 );
> +static_assert( std::is_same_v<std::tuple_element_t<3, IS1>, std::size_t> );
> +static_assert( std::get<7>(IS1{}) == 7 );
> +static_assert( destructure_sum(IS1{}) == 45 );
> +
> +using IS2 = std::integer_sequence<int, 42, 101, -13>;
> +static_assert( std::tuple_size_v<IS2> == 3 );
> +static_assert( std::is_same_v<std::tuple_element_t<1, IS2>, int> );
> +static_assert( std::get<2>(IS2{}) == -13 );
> +static_assert( destructure_sum(IS2{}) == 130 );
> +
> +using IS3 = std::integer_sequence<char>;
> +static_assert( std::tuple_size_v<IS3> == 0 );
> +
> +template<typename = void>
> +constexpr bool
> +test_basic()
> +{
> + {
> + auto [...elems] = std::make_index_sequence<10>{};
> +
> + static_assert( sizeof...(elems) == 10 );
> +
> + VERIFY( elems...[0] == 0 );
> + VERIFY( elems...[3] == 3 );
> + VERIFY( elems...[9] == 9 );
> + }
> +
> + {
> + auto [...elems] = std::integer_sequence<int, 3, 5, 7, 11>{};
> +
> + static_assert( sizeof...(elems) == 4 );
> +
> + VERIFY( elems...[0] == 3 );
> + VERIFY( elems...[1] == 5 );
> + VERIFY( elems...[2] == 7 );
> + VERIFY( elems...[3] == 11 );
> + }
> +
> + {
> + static constexpr auto [...elems] = std::integer_sequence<short, 2, 4, 8, 16>{};
> +
> + static_assert( sizeof...(elems) == 4 );
> +
> + // these fail
> + static_assert( elems...[0] == 2 );
> + static_assert( elems...[1] == 4 );
> + static_assert( elems...[2] == 8 );
> + static_assert( elems...[3] == 16 );
> + }
> +
> + return true;
> +}
> +
> +static_assert( test_basic() );
> --
> 2.43.0
>
More information about the Libstdc++
mailing list