[PATCH v2] libstdc++: Implement structured binding support for integer_sequence
Matthias Wippich
mfwippich@gmail.com
Thu Apr 9 14:32:06 GMT 2026
whoops, sorry for the extraneous whitespace. Thanks for taking care of
it. There's a typo in the updated changelog: `partrial` should be
`partial`.
In libc++ we did put `[[nodiscard]]` on `get` as an extension. Would
that be desirable for libstdc++ as well?
On Thu, Apr 9, 2026 at 1:03 PM Tomasz Kaminski <tkaminsk@redhat.com> wrote:
>
>
>
> On Thu, Apr 9, 2026 at 10:04 AM Tomasz Kamiński <tkaminsk@redhat.com> 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?
>
> All test passed (noting this also for myself as I have many patches in flight).
>>
>>
>> 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
>> + 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 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