[PATCH v2] libstdc++: Implement structured binding support for integer_sequence
Tomasz Kaminski
tkaminsk@redhat.com
Tue Apr 14 11:06:57 GMT 2026
On Tue, Apr 14, 2026 at 11:50 AM Jonathan Wakely <jwakely@redhat.com> wrote:
> On Thu, 09 Apr 2026 at 10:01 +0200, Tomasz Kamiński wrote:
> >From: Matthias Wippich <mfwippich@gmail.com>
> >
> >This implements P1789R3 Library Support for Expansion Statements.
> >https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p1789r3.pdf
> >
> >libstdc++-v3/ChangeLog:
> >
> > * include/bits/utility.h (std::tuple_size, std::tuple_element):
> > Add partrial specializations for integer_sequence.
> > (std::get(integer_sequence<_Tp, _Idx...>)): Define.
> > * include/bits/version.def (integer_sequence): Bump to 202511L.
> > * include/bits/version.h: Regenerate.
> > * testsuite/20_util/integer_sequence/tuple_access.cc: New test.
> > * testsuite/20_util/integer_sequence/tuple_access_neg.cc: New test.
> > * testsuite/experimental/feat-cxx14.cc: Updated check for
> > __cpp_lib_integer_sequence value.
> >
> >Co-authored-by: Ivan Lazaric <ivan.lazaric1@gmail.com>
> >Signed-off-by: Matthias Wippich <mfwippich@gmail.com>
> >---
> >v3:
> > - updates changelog
> > - fixes __cpp_lib_integer_sequence check in feat-cxx14.cc
> > - replaces dg-excess-errors in tuple_access_neg.cc
> > - add few testcasse to tuple_access.cc
> >
> >Testing on x86_64-linux. OK for trunk when all test passes?
> >
> > libstdc++-v3/include/bits/utility.h | 30 ++++
> > libstdc++-v3/include/bits/version.def | 5 +
> > libstdc++-v3/include/bits/version.h | 7 +-
> > .../20_util/integer_sequence/tuple_access.cc | 143 ++++++++++++++++++
> > .../integer_sequence/tuple_access_neg.cc | 22 +++
> > .../testsuite/experimental/feat-cxx14.cc | 4 +-
> > 6 files changed, 208 insertions(+), 3 deletions(-)
> > create mode 100644
> libstdc++-v3/testsuite/20_util/integer_sequence/tuple_access.cc
> > create mode 100644
> libstdc++-v3/testsuite/20_util/integer_sequence/tuple_access_neg.cc
> >
> >diff --git a/libstdc++-v3/include/bits/utility.h
> b/libstdc++-v3/include/bits/utility.h
> >index 93e9e9f9dba..363aed478c5 100644
> >--- a/libstdc++-v3/include/bits/utility.h
> >+++ b/libstdc++-v3/include/bits/utility.h
> >@@ -481,6 +481,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > static constexpr size_t size() noexcept { return sizeof...(_Idx); }
> > };
> >
> >+#if __glibcxx_integer_sequence >= 202511L // C++ >= 26
> >+
> >+ /// Structured binding support
>
> So that it has a bit more context in the generated Doxygen HTML,
> please make this comment:
>
> /** @brief Structured binding support for `integer_sequence`
>
> * @since C++26
> * @{
> */
>
> and then after the get overload, add /// @}
>
> That will create a group so that the same comment applies to all of
> tuple_size, tuple_element, and get.
>
> >+ 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
>
> I agree with the suggestion to add [[nodiscard]] here.
>
> OK with those changes (and the "partial" typo that Matthias noticed).
>
> Thanks to all three of you for doing this.
>
Merged to trunk. Thanks!
>
> >+ {
> >+ 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 cfb90533ce4..8db3c1d2df3 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 22dd31b9d32..455d7190e90 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/tuple_access.cc
> b/libstdc++-v3/testsuite/20_util/integer_sequence/tuple_access.cc
> >new file mode 100644
> >index 00000000000..afa644e7dab
> >--- /dev/null
> >+++ b/libstdc++-v3/testsuite/20_util/integer_sequence/tuple_access.cc
> >@@ -0,0 +1,143 @@
> >+// { dg-do compile { target c++26 } }
> >+
> >+#include <utility>
> >+#include <tuple>
> >+#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 );
> >+static_assert( noexcept(get<0>(IS1{})) );
> >+
> >+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 );
> >+static_assert( noexcept(get<0>(IS2{})) );
> >+
> >+using IS3 = std::integer_sequence<char>;
> >+static_assert( std::tuple_size_v<IS3> == 0 );
> >+
> >+using IS4 = std::integer_sequence<int, 1, 2>;
> >+static_assert( !std::is_constructible_v<std::pair<int, int>, IS4> );
> >+static_assert( !std::is_constructible_v<std::tuple<int, int>, IS4> );
> >+
> >+template<typename = void>
> >+constexpr bool
> >+test_basic()
> >+{
> >+ {
> >+ auto [...elems] = std::make_index_sequence<0>{};
> >+ static_assert( sizeof...(elems) == 0 );
> >+ }
> >+
> >+ {
> >+ 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 );
> >+
> >+ static_assert( elems...[0] == 2 );
> >+ static_assert( elems...[1] == 4 );
> >+ static_assert( elems...[2] == 8 );
> >+ static_assert( elems...[3] == 16 );
> >+ }
> >+
> >+ {
> >+ constexpr auto [...elems] = std::integer_sequence<int, 1, 8, 2,
> 11>{};
> >+
> >+ static_assert( sizeof...(elems) == 4 );
> >+
> >+ static_assert( elems...[0] == 1 );
> >+ static_assert( elems...[1] == 8 );
> >+ static_assert( elems...[2] == 2 );
> >+ static_assert( elems...[3] == 11 );
> >+ }
> >+
> >+ {
> >+ static constexpr auto&& [...elems] = std::integer_sequence<short, 2,
> 4, 8, 16>{};
> >+
> >+ static_assert( sizeof...(elems) == 4 );
> >+
> >+ static_assert( elems...[0] == 2 );
> >+ static_assert( elems...[1] == 4 );
> >+ static_assert( elems...[2] == 8 );
> >+ static_assert( elems...[3] == 16 );
> >+ }
> >+
> >+ /* Unimplemented, see PR117784
> >+ {
> >+ constexpr auto&& [...elems] = std::integer_sequence<int, 1, 8, 2,
> 11>{};
> >+
> >+ static_assert( sizeof...(elems) == 4 );
> >+
> >+ static_assert( elems...[0] == 1 );
> >+ static_assert( elems...[1] == 8 );
> >+ static_assert( elems...[2] == 2 );
> >+ static_assert( elems...[3] == 11 );
> >+ }
> >+ */
> >+
> >+ {
> >+ auto idx = 0;
> >+ static constexpr auto seq = std::make_index_sequence<4>{};
> >+ template for (constexpr auto elem : seq)
> >+ {
> >+ VERIFY( elem == idx );
> >+ ++idx;
> >+ }
> >+ VERIFY( idx == 4 );
> >+ }
> >+
> >+
> >+ /* Unimplemented, see PR117784
> >+ {
> >+ auto idx = 0;
> >+ constexpr auto seq = std::make_index_sequence<4>{};
> >+ template for (constexpr auto elem : seq)
> >+ {
> >+ VERIFY( elem == idx );
> >+ ++idx;
> >+ }
> >+ VERIFY( idx == 4 );
> >+ }
> >+
> >+ {
> >+ auto idx = 0;
> >+ template for (constexpr auto elem : std::make_index_sequence<4>{})
> >+ {
> >+ VERIFY( elem == idx );
> >+ ++idx;
> >+ }
> >+ VERIFY( idx == 4 );
> >+ }
> >+ */
> >+
> >+ return true;
> >+}
> >+
> >+static_assert( test_basic() );
> >diff --git
> a/libstdc++-v3/testsuite/20_util/integer_sequence/tuple_access_neg.cc
> b/libstdc++-v3/testsuite/20_util/integer_sequence/tuple_access_neg.cc
> >new file mode 100644
> >index 00000000000..8640114e7d3
> >--- /dev/null
> >+++ b/libstdc++-v3/testsuite/20_util/integer_sequence/tuple_access_neg.cc
> >@@ -0,0 +1,22 @@
> >+// { dg-do compile { target c++26 } }
> >+
> >+#include <utility>
> >+
> >+using empty = std::integer_sequence<int>;
> >+static_assert( std::is_same_v<std::tuple_element_t<0, empty>, int> ); //
> { dg-error "here" }
> >+static_assert( std::is_same_v<std::tuple_element_t<0, const empty>, int>
> ); // { dg-error "here" }
> >+
> >+using size4 = std::integer_sequence<int, 1, 9, 7, 15>;
> >+static_assert( std::is_same_v<std::tuple_element_t<4, size4>, int> ); //
> { dg-error "here" }
> >+static_assert( std::is_same_v<std::tuple_element_t<4, const size4>, int>
> ); // { dg-error "here" }
> >+
> >+void
> >+test()
> >+{
> >+ (void)std::get<0>(empty{}); // { dg-error "here" }
> >+ (void)std::get<4>(size4{}); // { dg-error "here" }
> >+}
> >+
> >+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
> >+// { dg-error "cannot index an empty pack" "" { target *-*-* } 0 }
> >+// { dg-error "pack index '.' is out of range for pack of length" "" {
> target *-*-* } 0 }
> >diff --git a/libstdc++-v3/testsuite/experimental/feat-cxx14.cc
> b/libstdc++-v3/testsuite/experimental/feat-cxx14.cc
> >index c009062b55c..4cc56c5f76e 100644
> >--- a/libstdc++-v3/testsuite/experimental/feat-cxx14.cc
> >+++ b/libstdc++-v3/testsuite/experimental/feat-cxx14.cc
> >@@ -18,8 +18,8 @@
> >
> > #ifndef __cpp_lib_integer_sequence
> > # error "__cpp_lib_integer_sequence"
> >-#elif __cpp_lib_integer_sequence != 201304
> >-# error "__cpp_lib_integer_sequence != 201304"
> >+#elif __cpp_lib_integer_sequence < 201304
> >+# error "__cpp_lib_integer_sequence < 201304"
> > #endif
> >
> > #ifndef __cpp_lib_exchange_function
> >--
> >2.53.0
> >
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260414/5a8bbf65/attachment-0001.htm>
More information about the Libstdc++
mailing list