[PATCH v2] libstdc++: Implement structured binding support for integer_sequence

Jonathan Wakely jwakely@redhat.com
Tue Apr 14 09:50:49 GMT 2026


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.

>+    {
>+      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
>
>



More information about the Libstdc++ mailing list