See attached test.cpp. I prefer the VIA_ARRAY=1 solution (generalized to
arbitrary integer types).
g++ -std=c++26 -O2 -Wall -c test.cpp -ftime-report:
- GGC Total of 74M for -DVIA_ARRAY=0
- GGC Total of 67M for -DVIA_ARRAY=1
hyperfine says:
Benchmark 1: ~/.local/gcc-16/bin/g++ -std=c++26 -O2 -Wall -c test.cpp
-DVIA_ARRAY=0
Time (mean ± σ): 683.7 ms ± 86.2 ms [User: 621.3 ms, System: 58.9 ms]
Range (min … max): 509.6 ms … 779.7 ms 10 runs
Benchmark 2: ~/.local/gcc-16/bin/g++ -std=c++26 -O2 -Wall -c test.cpp
-DVIA_ARRAY=1
Time (mean ± σ): 418.0 ms ± 172.2 ms [User: 376.9 ms, System: 39.2 ms]
Range (min … max): 264.2 ms … 703.2 ms 10 runs
Summary
~/.local/gcc-16/bin/g++ -std=c++26 -O2 -Wall -c test.cpp -DVIA_ARRAY=1 ran
1.64 ± 0.70 times faster than ~/.local/gcc-16/bin/g++ -std=c++26 -O2 -Wall
-c test.cpp -DVIA_ARRAY=0
- Matthias
Tomasz Kamiński [Monday, 13 October 2025, 14:40:33 CEST]:
> This patch introduces the internal helper type _IndexPack to support
> introducing a pack of indices via a structured binding declaration:
> static constexpr auto [...__is] = _IndexPack<N>();
>
> _IndexPack is a distinct type from _Index_tuple and index_sequence to
> enable a simpler implementation: it requires only a single template
> parameter, and get<I> performs returns I. Implementing get as a
> static member avoids making it visible for ADL overload resolution.
>
> The use of static on the structured binding is required to work around
> PR122269 [1].
>
> [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=122269
>
> libstdc++-v3/ChangeLog:
>
> * include/bits/utility.h (_IndexPack, tuple_size<_IndexPack<_Num>>)
> (tuple_element<_Idx, _IndexPack<_Num>>): Define.
> * testsuite/ext/indexpack.cc: New test.
> ---
> Tested on x86_64-linux locally. OK for trunk?
>
> libstdc++-v3/include/bits/utility.h | 19 +++++++++++++++++++
> libstdc++-v3/testsuite/ext/indexpack.cc | 19 +++++++++++++++++++
> 2 files changed, 38 insertions(+)
> create mode 100644 libstdc++-v3/testsuite/ext/indexpack.cc
>
> diff --git a/libstdc++-v3/include/bits/utility.h
> b/libstdc++-v3/include/bits/utility.h index 4e574658eba..2bc8629cb92 100644
> --- a/libstdc++-v3/include/bits/utility.h
> +++ b/libstdc++-v3/include/bits/utility.h
> @@ -313,6 +313,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> inline constexpr bool __is_nontype_v<nontype_t<__val>> = true;
> #endif
>
> +#if __cpp_structured_bindings >= 202411L
> + template<size_t _Num>
> + struct _IndexPack
> + {
> + template<size_t _Idx>
> + static consteval size_t
> + get() noexcept
> + { return _Idx; }
> + };
> +
> + template<size_t _Num>
> + struct tuple_size<_IndexPack<_Num>>
> + { static constexpr size_t value = _Num; };
> +
> + template<size_t _Idx, size_t _Num>
> + struct tuple_element<_Idx, _IndexPack<_Num>>
> + { using type = size_t; };
> +#endif
> +
> _GLIBCXX_END_NAMESPACE_VERSION
> } // namespace
>
> diff --git a/libstdc++-v3/testsuite/ext/indexpack.cc
> b/libstdc++-v3/testsuite/ext/indexpack.cc new file mode 100644
> index 00000000000..929d77e6bf1
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/ext/indexpack.cc
> @@ -0,0 +1,19 @@
> +// { dg-do compile { target c++26 } }
> +
> +#include <utility>
> +
> +template<std::size_t N>
> +void test()
> +{
> + // PR122269 static should not be necessary
> + static constexpr auto [...ids] = std::_IndexPack<N>();
> + static_assert( sizeof...(ids) == N );
> + static_assert( (ids + ... + 0) == N*(N-1)/2 );
> +}
> +
> +int main()
> +{
> + test<0>();
> + test<4>();
> + test<8>();
> +}
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Center for Heavy Ion Research https://gsi.de
std::simd
──────────────────────────────────────────────────────────────────────────
#include <bits/utility.h>
#include <span>
namespace std
{
#if VIA_ARRAY
template <size_t N>
inline constexpr size_t
index_pack[N] = {__integer_pack(size_t(N))...};
#else
template<size_t _Num>
struct index_pack
{
template<size_t _Idx>
static consteval const size_t&
get() noexcept
{
static constexpr size_t val = _Idx;
return val;
}
};
template<size_t _Num>
struct tuple_size<index_pack<_Num>>
{ static constexpr size_t value = _Num; };
template<size_t _Idx, size_t _Num>
struct tuple_element<_Idx, index_pack<_Num>>
{ using type = size_t; };
#endif
}
template <std::size_t N>
unsigned f(std::span<const int, N> data)
{
constexpr auto [...is] = std::index_pack<N>
#if VIA_ARRAY
;
#else
();
#endif
return (data[is] + ...);
}
unsigned g(std::span<const int> data)
{
return f(data.subspan<0, 1>())
+ f(data.subspan<0, 2>())
+ f(data.subspan<0, 3>())
+ f(data.subspan<0, 4>())
+ f(data.subspan<0, 5>())
+ f(data.subspan<0, 6>())
+ f(data.subspan<0, 7>())
+ f(data.subspan<0, 8>())
+ f(data.subspan<0, 9>())
+ f(data.subspan<0, 10>())
+ f(data.subspan<0, 11>())
+ f(data.subspan<0, 12>())
+ f(data.subspan<0, 13>())
+ f(data.subspan<0, 14>())
+ f(data.subspan<0, 15>())
+ f(data.subspan<0, 16>())
+ f(data.subspan<0, 17>())
+ f(data.subspan<0, 18>())
+ f(data.subspan<0, 19>())
+ f(data.subspan<0, 20>())
+ f(data.subspan<0, 21>())
+ f(data.subspan<0, 22>())
+ f(data.subspan<0, 23>())
+ f(data.subspan<0, 24>())
+ f(data.subspan<0, 25>())
+ f(data.subspan<0, 26>())
+ f(data.subspan<0, 27>())
+ f(data.subspan<0, 28>())
+ f(data.subspan<0, 29>());
}