libstdc++: Implement P1789R3: structured bindings for std::integer_sequence

Message ID 20260404170122.2928689-2-ivan.lazaric1@gmail.com
State New
Headers
Series 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

Ivan Lazaric April 4, 2026, 5:01 p.m. UTC
  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

Tomasz Kaminski April 5, 2026, 9:46 a.m. UTC | #1
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
>
>
  
Ivan Lazaric April 5, 2026, 12:11 p.m. UTC | #2
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
>>
>>
  
Jakub Jelinek April 5, 2026, 3:56 p.m. UTC | #3
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
  

Patch

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;
+  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);