libstdc++: Implement P1789R3: structured bindings for std::integer_sequence
Checks
| Context |
Check |
Description |
| linaro-tcwg-bot/tcwg_gcc_build--master-arm |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_gcc_check--master-arm |
fail
|
Test failed
|
| linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 |
fail
|
Test failed
|
Commit Message
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>
---
Tested everything under 20_util, including the new test.
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 | 29 +++++++++++++++++++
4 files changed, 68 insertions(+), 1 deletion(-)
create mode 100644 libstdc++-v3/testsuite/20_util/integer_sequence/structured_binding.cc
Comments
On Sat, Apr 4, 2026 at 7:03 PM Ivan Lazaric <ivan.lazaric1@gmail.com> wrote:
> 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>
> ---
> Tested everything under 20_util, including the new test.
>
> 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 | 29 +++++++++++++++++++
> 4 files changed, 68 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 b89e60d156d..6103f6a41d1 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 d73b547e1b0..0de2ad9e5f3 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..b8e9fa1caa1
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/integer_sequence/structured_binding.cc
> @@ -0,0 +1,29 @@
> +// { dg-do compile { target c++26 } }
> +
> +#include <utility>
> +
> +#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;
There is already patch for this feature from Matthias Wippic:
https://gcc.gnu.org/pipermail/libstdc++/2025-November/064271.html
However, it got stuck because before the Croydon meeting, as currently
specified the following is ill-formed:
auto [...elems] == std::make_integer_sequence<10>{};
(Could you add test like above):
This was addressed by CWG3135 (
https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#3135)
that was accepted in Croydon, but I do not think we should ship that
feature before it is resolved on trunk.
+ 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>, 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);
> --
> 2.43.0
>
>
Will post an updated patch, but wouldn’t pre-CWG3135 wording also work, due
to lifetime extension?
auto [a, b, c] = std::make_index_sequence<3>{};// as-ifauto seq =
std::make_index_sequence<3>{};
std::size_t&& a = std::get<0>(seq);
std::size_t&& b = std::get<1>(seq);
std::size_t&& c = std::get<2>(seq);
On Sun, Apr 5, 2026 at 11:46 AM Tomasz Kaminski <tkaminsk@redhat.com> wrote:
>
>
> On Sat, Apr 4, 2026 at 7:03 PM Ivan Lazaric <ivan.lazaric1@gmail.com>
> wrote:
>
>> 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>
>> ---
>> Tested everything under 20_util, including the new test.
>>
>> 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 | 29 +++++++++++++++++++
>> 4 files changed, 68 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 b89e60d156d..6103f6a41d1 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 d73b547e1b0..0de2ad9e5f3 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..b8e9fa1caa1
>> --- /dev/null
>> +++
>> b/libstdc++-v3/testsuite/20_util/integer_sequence/structured_binding.cc
>> @@ -0,0 +1,29 @@
>> +// { dg-do compile { target c++26 } }
>> +
>> +#include <utility>
>> +
>> +#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;
>
> There is already patch for this feature from Matthias Wippic:
> https://gcc.gnu.org/pipermail/libstdc++/2025-November/064271.html
>
> However, it got stuck because before the Croydon meeting, as currently
> specified the following is ill-formed:
> auto [...elems] == std::make_integer_sequence<10>{};
> (Could you add test like above):
>
> This was addressed by CWG3135 (
> https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#3135)
> that was accepted in Croydon, but I do not think we should ship that
> feature before it is resolved on trunk.
>
>
> + 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>, 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);
>> --
>> 2.43.0
>>
>>
On Sun, Apr 05, 2026 at 11:46:01AM +0200, Tomasz Kaminski wrote:
> There is already patch for this feature from Matthias Wippic:
> https://gcc.gnu.org/pipermail/libstdc++/2025-November/064271.html
>
> However, it got stuck because before the Croydon meeting, as currently
> specified the following is ill-formed:
> auto [...elems] == std::make_integer_sequence<10>{};
> (Could you add test like above):
>
> This was addressed by CWG3135 (
> https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#3135)
> that was accepted in Croydon, but I do not think we should ship that
> feature before it is resolved on trunk.
https://gcc.gnu.org/pipermail/gcc-patches/2026-April/thread.html#712174
Jakub
@@ -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
@@ -184,6 +184,11 @@ ftms = {
ftms = {
name = integer_sequence;
+ values = {
+ v = 202511;
+ cxxmin = 26;
+ extra_cond = "__cpp_pack_indexing";
+ };
values = {
v = 201304;
cxxmin = 14;
@@ -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
new file mode 100644
@@ -0,0 +1,29 @@
+// { dg-do compile { target c++26 } }
+
+#include <utility>
+
+#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>, 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);