[PATCH] libstdc++: Optimize functor storage for transform views iterators.
Tomasz Kaminski
tkaminsk@redhat.com
Fri Nov 14 17:36:53 GMT 2025
>From other side, it would be fairly easy to add another _TrivlalEmpty
specialization for __func_handle,
that would be generated under p3117r1 conditions, which I do not like, as
they do not make even *it
on same iterator sable for the Obfuscating symbol.
I would go instead with is_empty_v<F> && copyable<F> &&
trivially_copyable<F>, and have [[no_unique_address]] F _M_fun
member.
>From that perspective, think we should land this patch regardless. Even if
we would like to do a follow up aligned with
additional specialization that is aligned the paper.
Regards,
Tomasz
On Fri, Nov 14, 2025 at 3:37 PM Tomasz Kaminski <tkaminsk@redhat.com> wrote:
>
>
> On Fri, Nov 14, 2025 at 2:18 PM Hewill Kang <hewillk@gmail.com> wrote:
>
>> I can't help but wonder, if this patch can't optimize for lambdas (users
>> generally don't explicitly spell `static`), then I don't see much point in
>> this optimization.
>>
> Then they should start to. if they want to get optimizations. For most
> cases it really does not matter, as the compiler inlines and removes all
> iterators.
> The only relevant cases here are erasing view (storing in any_view) or
> relying on range being borrowed.
>
>>
>> I think the case of `Obfuscate` is unlikely to occur. The view class will
>> move the original object to the `movable_box` via `std::move(fun)`, so
>> `this` pointer has already changed.
>>
> I believe the standard requires it to work, so breaking it would be
> invalid optimization.
>
>>
>> If we could actually create such a pretentious functor with a disgusting
>> operator(), I think the standard has every reason not to care about it.
>>
>> Therefore, after thinking about it, I think we should perhaps follow
>> p3117r1 and use `is_empty_v<F> && is_trivially_default_constructible_v<F>
>> && is_trivially_destructible_v<F>` to optimize lambda. Because that is
>> undoubtedly the most common use case (and the most worthy of
>> optimization). This is something this patch should be interested in.
>>
> Unless the standard gives permission to do so, I do not think such change
> is possible.
>
>>
>> Tomasz Kaminski <tkaminsk@redhat.com> 於 2025年11月14日 週五 下午5:36寫道:
>>
>>>
>>>
>>> On Fri, Nov 14, 2025 at 10:24 AM Hewill Kang <hewillk@gmail.com> wrote:
>>>
>>>> Yes, I do not think anything in standard clearly states the that
>>>>> following is
>>>>> allowed to fail:
>>>>> struct Obfuscate
>>>>> {
>>>>> int operator()(int x) const
>>>>> { return x + static_assert<unintptr_t>(this); }
>>>>> };
>>>>
>>>>
>>>>> std::vector<int> v{1, 2, 3, 4, 5};
>>>>> auto tv = v | std::transform(Obfuscate{});
>>>>> VERIFY( ranges::equal(tv, tv) );
>>>>> But storing an empty object in an iterator will break.
>>>>
>>>>
>>>> This is a very good observation, so recreating empty objects is still
>>>> problematic.
>>>> It also seems to suggest that
>>>> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3117r1.html (most
>>>> likely a follow-up to this patch) and
>>>> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3705r1.html
>>>> might have issues with this.
>>>> For example, `operator()` uses `this` or `operator==` uses `this`.
>>>>
>>> Yes, static operator() is bulletproof way of ensuring that it does not
>>> depend on object identity,
>>> even if the object itself is not empty. We do not need to use
>>> heuristics based on size anymore.
>>>
>>>>
>>>> Tomasz Kaminski <tkaminsk@redhat.com> 於 2025年11月14日 週五 下午2:36寫道:
>>>>
>>>>>
>>>>>
>>>>> On Fri, Nov 14, 2025 at 4:16 AM Patrick Palka <ppalka@redhat.com>
>>>>> wrote:
>>>>>
>>>>>> On Thu, 13 Nov 2025, Tomasz Kamiński wrote:
>>>>>>
>>>>>> > The iterators for transform views (views::transform,
>>>>>> views::zip_transform,
>>>>>> > and views::adjacent_transform) now store a __detail::__func_handle
>>>>>> instead
>>>>>> > of a pointer to the view object (_M_parent).
>>>>>> >
>>>>>> > The behavior of the __func_handle specialization depends on the
>>>>>> _FunctorType
>>>>>> > template parameter:
>>>>>> > * _FunctionPtr: Used if the functor is a function pointer. The
>>>>>> pointer is
>>>>>> > stored directly in __func_handle and the iterator, avoiding double
>>>>>> > indirection through a pointer to the function pointer.
>>>>>> > * _MemberPointer: Used for data or function member pointers. This
>>>>>> behaves
>>>>>> > similarly to _FunctionPtr, but uses __invoke for invocations.
>>>>>> > * _StaticOperator: Used if the operator() selected by overload
>>>>>> resolution
>>>>>> > for the iterator reference is static. In this case,
>>>>>> __func_handle is empty,
>>>>>> > reducing the iterator size.
>>>>>> > * _Statefull: Used for all remaining cases. __func_handle stores a
>>>>>> pointer
>>>>>> > to the functor object stored within the view. Only for this
>>>>>> specialization is
>>>>>> > the cv-qualification of the functor template parameter (_Fn)
>>>>>> relevant, and
>>>>>> > both const and mutable specializations are generated.
>>>>>>
>>>>>> FWIW it's spelled 'stateful' with just one l.
>>>>>>
>>>>>> I guess you concluded that this optimization is unsafe for empty
>>>>>> objects
>>>>>> with non-static operator()?
>>>>>>
>>>>> Yes, I do not think anything in standard clearly states the that
>>>>> following is
>>>>> allowed to fail:
>>>>> struct Obfuscate
>>>>> {
>>>>> int operator()(int x) const
>>>>> { return x + static_assert<unintptr_t>(this); }
>>>>> };
>>>>>
>>>>> std::vector<int> v{1, 2, 3, 4, 5};
>>>>> auto tv = v | std::transform(Obfuscate{});
>>>>> VERIFY( ranges::equal(tv, tv) );
>>>>>
>>>>> But storing an empty object in an iterator will break.
>>>>>
>>>>>
>>>>>> >
>>>>>> > As a consequence of these changes, the iterators of transform views
>>>>>> no longer
>>>>>> > depend on the view object when __func_handle is specialized for
>>>>>> values other
>>>>>> > than _Statefull. The corresponding views are not marked as
>>>>>> borrowed_range,
>>>>>> > as they are not marked as such in the standard.
>>>>>> >
>>>>>> > Storing function member pointers directly increases the iterator
>>>>>> size in that
>>>>>> > specific case, but this is deemed beneficial for consistent
>>>>>> treatment of
>>>>>> > function and data member pointers.
>>>>>> >
>>>>>> > To avoid materializing temporaries when the underlying iterator(s)
>>>>>> return a
>>>>>> > prvalue, the _M_call_deref and _M_call_subscript methods of
>>>>>> __func_handle are
>>>>>> > defined to accept the iterator(s), which are then dereferenced as
>>>>>> arguments
>>>>>> > of the functor.
>>>>>> >
>>>>>> > Using _Fd::operator()(*__iters...) inside requires expression is
>>>>>> only
>>>>>> > supported since clang-20, however at the point of GCC-16 release,
>>>>>> clang-22
>>>>>> > should be already available.
>>>>>> >
>>>>>> > libstdc++-v3/ChangeLog:
>>>>>> >
>>>>>> > * include/std/ranges (__detail::__func_handle)
>>>>>> > (__detail::func_handle_t): Define.
>>>>>> > (transform_view::_Iterator, zip_transform_view::_Iterator)
>>>>>> > (adjacent_tranform_view::_Iterator): Replace pointer to view
>>>>>> > (_M_parent) with pointer to functor (_M_fun). Update
>>>>>> constructors
>>>>>> > to cosntruct _M_fun from *__parent->_M_fun. Define operator*
>>>>>> and
>>>>>> > operator[] in terms of _M_call_deref and _M_call_subscript.
>>>>>> > * testsuite/std/ranges/adaptors/adjacent_transform/1.cc: New
>>>>>> tests.
>>>>>> > * testsuite/std/ranges/adaptors/transform.cc: New tests.
>>>>>> > * testsuite/std/ranges/zip_transform/1.cc: New tests.
>>>>>> > ---
>>>>>> > Tested transform view files locally. More testing in progress.
>>>>>> >
>>>>>> > libstdc++-v3/include/std/ranges | 214
>>>>>> +++++++++++++++---
>>>>>> > .../ranges/adaptors/adjacent_transform/1.cc | 97 ++++++++
>>>>>> > .../std/ranges/adaptors/transform.cc | 118 +++++++++-
>>>>>> > .../testsuite/std/ranges/zip_transform/1.cc | 101 +++++++++
>>>>>> > 4 files changed, 493 insertions(+), 37 deletions(-)
>>>>>> >
>>>>>> > diff --git a/libstdc++-v3/include/std/ranges
>>>>>> b/libstdc++-v3/include/std/ranges
>>>>>> > index ae57b9a0809..28b4f1d2b90 100644
>>>>>> > --- a/libstdc++-v3/include/std/ranges
>>>>>> > +++ b/libstdc++-v3/include/std/ranges
>>>>>> > @@ -286,6 +286,138 @@ namespace ranges
>>>>>> > operator->() const noexcept
>>>>>> > { return std::__addressof(_M_value); }
>>>>>> > };
>>>>>> > +
>>>>>> > + enum class _FunctorType
>>>>>> > + {
>>>>>> > + _FunctionPtr,
>>>>>> > + _MemberPtr,
>>>>>> > + _StaticOperator,
>>>>>> > + _Statefull,
>>>>>> > + };
>>>>>> > +
>>>>>> > + template<typename _Fn, _FunctorType __ft>
>>>>>> > + struct __func_handle
>>>>>> > + {
>>>>>> > + __func_handle() = default;
>>>>>> > +
>>>>>> > + constexpr explicit
>>>>>> > + __func_handle(_Fn& __func) noexcept
>>>>>> > + : _M_ptr(std::addressof(__func))
>>>>>> > + { }
>>>>>> > +
>>>>>> > + template<typename _Un>
>>>>>> > + requires (!is_const_v<_Un>) && is_same_v<const _Un, _Fn>
>>>>>> > + constexpr explicit
>>>>>> > + __func_handle(__func_handle<_Un, __ft> __other) noexcept
>>>>>> > + : _M_ptr(__other._M_ptr)
>>>>>> > + { }
>>>>>> > +
>>>>>> > + template<typename... _Iters>
>>>>>> > + constexpr decltype(auto)
>>>>>> > + _M_call_deref(const _Iters&... __iters) const
>>>>>> > + noexcept(noexcept((*_M_ptr)(*__iters...)))
>>>>>> > + { return (*_M_ptr)(*__iters...); }
>>>>>> > +
>>>>>> > + template<typename _DistType, typename... _Iters>
>>>>>> > + constexpr decltype(auto)
>>>>>> > + _M_call_subscript(const _DistType __n, const _Iters&...
>>>>>> __iters) const
>>>>>> > +
>>>>>> noexcept(noexcept((*_M_ptr)(__iters[iter_difference_t<_Iters>(__n)]...)))
>>>>>> > + { return
>>>>>> (*_M_ptr)(__iters[iter_difference_t<_Iters>(__n)]...); }
>>>>>> > +
>>>>>> > + private:
>>>>>> > + _Fn* _M_ptr = nullptr;
>>>>>> > +
>>>>>> > + template<typename, _FunctorType>
>>>>>> > + friend struct __func_handle;
>>>>>> > + };
>>>>>> > +
>>>>>> > + template<typename _Fn>
>>>>>> > + struct __func_handle<_Fn, _FunctorType::_FunctionPtr>
>>>>>> > + {
>>>>>> > + __func_handle() = default;
>>>>>> > +
>>>>>> > + constexpr explicit
>>>>>> > + __func_handle(_Fn __func) noexcept
>>>>>> > + : _M_ptr(__func)
>>>>>> > + { }
>>>>>> > +
>>>>>> > + template<typename... _Iters>
>>>>>> > + constexpr decltype(auto)
>>>>>> > + _M_call_deref(const _Iters&... __iters) const
>>>>>> > + noexcept(noexcept(_M_ptr(*__iters...)))
>>>>>> > + { return _M_ptr(*__iters...); }
>>>>>> > +
>>>>>> > + template<typename _DistType, typename... _Iters>
>>>>>> > + constexpr decltype(auto)
>>>>>> > + _M_call_subscript(const _DistType __n, const _Iters&...
>>>>>> __iters) const
>>>>>> > +
>>>>>> noexcept(noexcept(_M_ptr(__iters[iter_difference_t<_Iters>(__n)]...)))
>>>>>> > + { return
>>>>>> _M_ptr(__iters[iter_difference_t<_Iters>(__n)]...); }
>>>>>> > +
>>>>>> > + private:
>>>>>> > + _Fn _M_ptr = nullptr;
>>>>>> > + };
>>>>>> > +
>>>>>> > + template<typename _Fn>
>>>>>> > + struct __func_handle<_Fn, _FunctorType::_MemberPtr>
>>>>>> > + {
>>>>>> > + __func_handle() = default;
>>>>>> > +
>>>>>> > + constexpr explicit
>>>>>> > + __func_handle(_Fn __func) noexcept
>>>>>> > + : _M_ptr(__func)
>>>>>> > + {}
>>>>>> > +
>>>>>> > + template<typename... _Iters>
>>>>>> > + constexpr decltype(auto)
>>>>>> > + _M_call_deref(const _Iters&... __iters) const
>>>>>> > + noexcept(noexcept(std::__invoke(_M_ptr, *__iters...)))
>>>>>> > + { return std::__invoke(_M_ptr, *__iters...); }
>>>>>> > +
>>>>>> > + template<typename _DistType, typename... _Iters>
>>>>>> > + constexpr decltype(auto)
>>>>>> > + _M_call_subscript(const _DistType __n, const _Iters&...
>>>>>> __iters) const
>>>>>> > + noexcept(noexcept(std::__invoke(_M_ptr,
>>>>>> __iters[iter_difference_t<_Iters>(__n)]...)))
>>>>>> > + { return std::__invoke(_M_ptr,
>>>>>> __iters[iter_difference_t<_Iters>(__n)]...); }
>>>>>> > +
>>>>>> > + private:
>>>>>> > + _Fn _M_ptr = nullptr;
>>>>>> > + };
>>>>>> > +
>>>>>> > + template<typename _Fn>
>>>>>> > + struct __func_handle<_Fn, _FunctorType::_StaticOperator>
>>>>>> > + {
>>>>>> > + __func_handle() = default;
>>>>>> > +
>>>>>> > + constexpr explicit
>>>>>> > + __func_handle(const _Fn&) noexcept
>>>>>> > + {}
>>>>>> > +
>>>>>> > + template<typename... _Iters>
>>>>>> > + static constexpr decltype(auto)
>>>>>> > + _M_call_deref(const _Iters&... __iters)
>>>>>> > + noexcept(noexcept(_Fn::operator()(*__iters...)))
>>>>>> > + { return _Fn::operator()(*__iters...); }
>>>>>> > +
>>>>>> > + template<typename _DistType, typename... _Iters>
>>>>>> > + static constexpr decltype(auto)
>>>>>> > + _M_call_subscript(_DistType __n, const _Iters&... __iters)
>>>>>> > +
>>>>>> noexcept(noexcept(_Fn::operator()(__iters[iter_difference_t<_Iters>(__n)]...)))
>>>>>> > + { return
>>>>>> _Fn::operator()(__iters[iter_difference_t<_Iters>(__n)]...); }
>>>>>> > + };
>>>>>> > +
>>>>>> > + template<typename _Fn, typename... _Iters>
>>>>>> > + using __func_handle_t = decltype([] {
>>>>>> > + using _Fd = remove_cv_t<_Fn>;
>>>>>> > + if constexpr (is_member_pointer_v<_Fd>)
>>>>>> > + return __func_handle<_Fd, _FunctorType::_MemberPtr>();
>>>>>> > + else if constexpr
>>>>>> (std::is_function_v<remove_pointer_t<_Fd>>)
>>>>>> > + return __func_handle<_Fd, _FunctorType::_FunctionPtr>();
>>>>>> > + else if constexpr (requires (const _Iters&... __iters)
>>>>>> > + { _Fd::operator()(*__iters...); })
>>>>>> > + return __func_handle<_Fd,
>>>>>> _FunctorType::_StaticOperator>();
>>>>>> > + else
>>>>>> > + return __func_handle<_Fn, _FunctorType::_Statefull>();
>>>>>> > + }());
>>>>>> > } // namespace __detail
>>>>>> >
>>>>>> > /// A view that contains exactly one element.
>>>>>> > @@ -1874,6 +2006,10 @@ namespace views::__adaptor
>>>>>> > private:
>>>>>> > using _Parent = __detail::__maybe_const_t<_Const,
>>>>>> transform_view>;
>>>>>> > using _Base = transform_view::_Base<_Const>;
>>>>>> > + using _Base_iter = iterator_t<_Base>;
>>>>>> > + using _Func_handle = __detail::__func_handle_t<
>>>>>> > + __detail::__maybe_const_t<_Const,
>>>>>> _Fp>,
>>>>>> > + _Base_iter>;
>>>>>> >
>>>>>> > static auto
>>>>>> > _S_iter_concept()
>>>>>> > @@ -1888,10 +2024,8 @@ namespace views::__adaptor
>>>>>> > return input_iterator_tag{};
>>>>>> > }
>>>>>> >
>>>>>> > - using _Base_iter = iterator_t<_Base>;
>>>>>> > -
>>>>>> > _Base_iter _M_current = _Base_iter();
>>>>>> > - _Parent* _M_parent = nullptr;
>>>>>> > + [[no_unique_address]] _Func_handle _M_fun;
>>>>>> >
>>>>>> > public:
>>>>>> > using iterator_concept = decltype(_S_iter_concept());
>>>>>> > @@ -1904,16 +2038,20 @@ namespace views::__adaptor
>>>>>> > _Iterator() requires default_initializable<_Base_iter> =
>>>>>> default;
>>>>>> >
>>>>>> > constexpr
>>>>>> > - _Iterator(_Parent* __parent, _Base_iter __current)
>>>>>> > - : _M_current(std::move(__current)),
>>>>>> > - _M_parent(__parent)
>>>>>> > + _Iterator(_Func_handle __fun, _Base_iter __current)
>>>>>> > + : _M_current(std::move(__current)), _M_fun(__fun)
>>>>>> > { }
>>>>>> >
>>>>>> > + constexpr
>>>>>> > + _Iterator(_Parent* __parent, _Base_iter __current)
>>>>>> > + : _M_current(std::move(__current)),
>>>>>> _M_fun(*__parent->_M_fun)
>>>>>> > + {}
>>>>>> > +
>>>>>> > constexpr
>>>>>> > _Iterator(_Iterator<!_Const> __i)
>>>>>> > requires _Const
>>>>>> > && convertible_to<iterator_t<_Vp>, _Base_iter>
>>>>>> > - : _M_current(std::move(__i._M_current)),
>>>>>> _M_parent(__i._M_parent)
>>>>>> > + : _M_current(std::move(__i._M_current)),
>>>>>> _M_fun(__i._M_fun)
>>>>>> > { }
>>>>>> >
>>>>>> > constexpr const _Base_iter&
>>>>>> > @@ -1926,8 +2064,8 @@ namespace views::__adaptor
>>>>>> >
>>>>>> > constexpr decltype(auto)
>>>>>> > operator*() const
>>>>>> > - noexcept(noexcept(std::__invoke(*_M_parent->_M_fun,
>>>>>> *_M_current)))
>>>>>> > - { return std::__invoke(*_M_parent->_M_fun, *_M_current); }
>>>>>> > + noexcept(noexcept(_M_fun._M_call_deref(_M_current)))
>>>>>> > + { return _M_fun._M_call_deref(_M_current); }
>>>>>> >
>>>>>> > constexpr _Iterator&
>>>>>> > operator++()
>>>>>> > @@ -1980,7 +2118,7 @@ namespace views::__adaptor
>>>>>> > constexpr decltype(auto)
>>>>>> > operator[](difference_type __n) const
>>>>>> > requires random_access_range<_Base>
>>>>>> > - { return std::__invoke(*_M_parent->_M_fun,
>>>>>> _M_current[__n]); }
>>>>>> > + { return _M_fun._M_call_subscript(__n, _M_current); }
>>>>>> >
>>>>>> > friend constexpr bool
>>>>>> > operator==(const _Iterator& __x, const _Iterator& __y)
>>>>>> > @@ -2018,17 +2156,17 @@ namespace views::__adaptor
>>>>>> > friend constexpr _Iterator
>>>>>> > operator+(_Iterator __i, difference_type __n)
>>>>>> > requires random_access_range<_Base>
>>>>>> > - { return {__i._M_parent, __i._M_current + __n}; }
>>>>>> > + { return {__i._M_fun, __i._M_current + __n}; }
>>>>>> >
>>>>>> > friend constexpr _Iterator
>>>>>> > operator+(difference_type __n, _Iterator __i)
>>>>>> > requires random_access_range<_Base>
>>>>>> > - { return {__i._M_parent, __i._M_current + __n}; }
>>>>>> > + { return {__i._M_fun, __i._M_current + __n}; }
>>>>>> >
>>>>>> > friend constexpr _Iterator
>>>>>> > operator-(_Iterator __i, difference_type __n)
>>>>>> > requires random_access_range<_Base>
>>>>>> > - { return {__i._M_parent, __i._M_current - __n}; }
>>>>>> > + { return {__i._M_fun, __i._M_current - __n}; }
>>>>>> >
>>>>>> > // _GLIBCXX_RESOLVE_LIB_DEFECTS
>>>>>> > // 3483. transform_view::iterator's difference is
>>>>>> overconstrained
>>>>>> > @@ -5126,13 +5264,21 @@ namespace views::__adaptor
>>>>>> > class zip_transform_view<_Fp, _Vs...>::_Iterator : public
>>>>>> __iter_cat<_Const>
>>>>>> > {
>>>>>> > using _Parent = __detail::__maybe_const_t<_Const,
>>>>>> zip_transform_view>;
>>>>>> > + using _Fun_handle = __detail::__func_handle_t<
>>>>>> > + __detail::__maybe_const_t<_Const, _Fp>,
>>>>>> > +
>>>>>> iterator_t<__detail::__maybe_const_t<_Const, _Vs>>...>;
>>>>>> >
>>>>>> > - _Parent* _M_parent = nullptr;
>>>>>> > + [[no_unique_address]] _Fun_handle _M_fun;
>>>>>> > __ziperator<_Const> _M_inner;
>>>>>> >
>>>>>> > + constexpr
>>>>>> > + _Iterator(_Fun_handle __fun, __ziperator<_Const> __inner)
>>>>>> > + : _M_fun(__fun), _M_inner(std::move(__inner))
>>>>>> > + { }
>>>>>> > +
>>>>>> > constexpr
>>>>>> > _Iterator(_Parent& __parent, __ziperator<_Const> __inner)
>>>>>> > - : _M_parent(std::__addressof(__parent)),
>>>>>> _M_inner(std::move(__inner))
>>>>>> > + : _M_fun(*__parent._M_fun), _M_inner(std::move(__inner))
>>>>>> > { }
>>>>>> >
>>>>>> > friend class zip_transform_view;
>>>>>> > @@ -5150,14 +5296,14 @@ namespace views::__adaptor
>>>>>> > constexpr
>>>>>> > _Iterator(_Iterator<!_Const> __i)
>>>>>> > requires _Const && convertible_to<__ziperator<false>,
>>>>>> __ziperator<_Const>>
>>>>>> > - : _M_parent(__i._M_parent), _M_inner(std::move(__i._M_inner))
>>>>>> > + : _M_fun(__i._M_fun), _M_inner(std::move(__i._M_inner))
>>>>>> > { }
>>>>>> >
>>>>>> > constexpr decltype(auto)
>>>>>> > operator*() const
>>>>>> > {
>>>>>> > return std::apply([&](const auto&... __iters) ->
>>>>>> decltype(auto) {
>>>>>> > - return std::__invoke(*_M_parent->_M_fun, *__iters...);
>>>>>> > + return _M_fun._M_call_deref(__iters...);
>>>>>> > }, _M_inner._M_current);
>>>>>> > }
>>>>>> >
>>>>>> > @@ -5213,7 +5359,7 @@ namespace views::__adaptor
>>>>>> > operator[](difference_type __n) const requires
>>>>>> random_access_range<_Base<_Const>>
>>>>>> > {
>>>>>> > return std::apply([&]<typename... _Is>(const _Is&...
>>>>>> __iters) -> decltype(auto) {
>>>>>> > - return std::__invoke(*_M_parent->_M_fun,
>>>>>> __iters[iter_difference_t<_Is>(__n)]...);
>>>>>> > + return _M_fun._M_call_subscript(__n, __iters...);
>>>>>> > }, _M_inner._M_current);
>>>>>> > }
>>>>>> >
>>>>>> > @@ -5230,17 +5376,17 @@ namespace views::__adaptor
>>>>>> > friend constexpr _Iterator
>>>>>> > operator+(const _Iterator& __i, difference_type __n)
>>>>>> > requires random_access_range<_Base<_Const>>
>>>>>> > - { return _Iterator(*__i._M_parent, __i._M_inner + __n); }
>>>>>> > + { return _Iterator(__i._M_fun, __i._M_inner + __n); }
>>>>>> >
>>>>>> > friend constexpr _Iterator
>>>>>> > operator+(difference_type __n, const _Iterator& __i)
>>>>>> > requires random_access_range<_Base<_Const>>
>>>>>> > - { return _Iterator(*__i._M_parent, __i._M_inner + __n); }
>>>>>> > + { return _Iterator(__i._M_fun, __i._M_inner + __n); }
>>>>>> >
>>>>>> > friend constexpr _Iterator
>>>>>> > operator-(const _Iterator& __i, difference_type __n)
>>>>>> > requires random_access_range<_Base<_Const>>
>>>>>> > - { return _Iterator(*__i._M_parent, __i._M_inner - __n); }
>>>>>> > + { return _Iterator(__i._M_fun, __i._M_inner - __n); }
>>>>>> >
>>>>>> > friend constexpr difference_type
>>>>>> > operator-(const _Iterator& __x, const _Iterator& __y)
>>>>>> > @@ -5807,13 +5953,23 @@ namespace views::__adaptor
>>>>>> > {
>>>>>> > using _Parent = __detail::__maybe_const_t<_Const,
>>>>>> adjacent_transform_view>;
>>>>>> > using _Base = __detail::__maybe_const_t<_Const, _Vp>;
>>>>>> > + using _Fun_handle = decltype([]<size_t...
>>>>>> _Ids>(std::index_sequence<_Ids...>) {
>>>>>> > + return __detail::__func_handle_t<
>>>>>> > + __detail::__maybe_const_t<_Const,
>>>>>> _Fp>,
>>>>>> > +
>>>>>> iterator_t<__detail::__maybe_const_t<_Const || (_Ids < 0u), _Vp>>...>();
>>>>>>
>>>>>> Maybe '(_Ids, _Const)' would be a little less cryptic than
>>>>>> '_Const || (_Ids < 0u)'?
>>>>>>
>>>>> Yes, that is major improvement.
>>>>>
>>>>>> Besides that looks good!
>>>>>>
>>>>>> > + }(make_index_sequence<_Nm>()));
>>>>>> >
>>>>>> > - _Parent* _M_parent = nullptr;
>>>>>> > + [[no_unique_address]] _Fun_handle _M_fun;
>>>>>> > _InnerIter<_Const> _M_inner;
>>>>>> >
>>>>>> > + constexpr
>>>>>> > + _Iterator(_Fun_handle __fun, _InnerIter<_Const> __inner)
>>>>>> > + : _M_fun(__fun), _M_inner(std::move(__inner))
>>>>>> > + { }
>>>>>> > +
>>>>>> > constexpr
>>>>>> > _Iterator(_Parent& __parent, _InnerIter<_Const> __inner)
>>>>>> > - : _M_parent(std::__addressof(__parent)),
>>>>>> _M_inner(std::move(__inner))
>>>>>> > + : _M_fun(*__parent._M_fun), _M_inner(std::move(__inner))
>>>>>> > { }
>>>>>> >
>>>>>> > static auto
>>>>>> > @@ -5854,14 +6010,14 @@ namespace views::__adaptor
>>>>>> > constexpr
>>>>>> > _Iterator(_Iterator<!_Const> __i)
>>>>>> > requires _Const && convertible_to<_InnerIter<false>,
>>>>>> _InnerIter<_Const>>
>>>>>> > - : _M_parent(__i._M_parent), _M_inner(std::move(__i._M_inner))
>>>>>> > + : _M_fun(__i._M_fun), _M_inner(std::move(__i._M_inner))
>>>>>> > { }
>>>>>> >
>>>>>> > constexpr decltype(auto)
>>>>>> > operator*() const
>>>>>> > {
>>>>>> > return std::apply([&](const auto&... __iters) ->
>>>>>> decltype(auto) {
>>>>>> > - return std::__invoke(*_M_parent->_M_fun, *__iters...);
>>>>>> > + return _M_fun._M_call_deref(__iters...);
>>>>>> > }, _M_inner._M_current);
>>>>>> > }
>>>>>> >
>>>>>> > @@ -5913,7 +6069,7 @@ namespace views::__adaptor
>>>>>> > operator[](difference_type __n) const requires
>>>>>> random_access_range<_Base>
>>>>>> > {
>>>>>> > return std::apply([&](const auto&... __iters) ->
>>>>>> decltype(auto) {
>>>>>> > - return std::__invoke(*_M_parent->_M_fun, __iters[__n]...);
>>>>>> > + return _M_fun._M_call_subscript(__n, __iters...);
>>>>>> > }, _M_inner._M_current);
>>>>>> > }
>>>>>> >
>>>>>> > @@ -5950,17 +6106,17 @@ namespace views::__adaptor
>>>>>> > friend constexpr _Iterator
>>>>>> > operator+(const _Iterator& __i, difference_type __n)
>>>>>> > requires random_access_range<_Base>
>>>>>> > - { return _Iterator(*__i._M_parent, __i._M_inner + __n); }
>>>>>> > + { return _Iterator(__i._M_fun, __i._M_inner + __n); }
>>>>>> >
>>>>>> > friend constexpr _Iterator
>>>>>> > operator+(difference_type __n, const _Iterator& __i)
>>>>>> > requires random_access_range<_Base>
>>>>>> > - { return _Iterator(*__i._M_parent, __i._M_inner + __n); }
>>>>>> > + { return _Iterator(__i._M_fun, __i._M_inner + __n); }
>>>>>> >
>>>>>> > friend constexpr _Iterator
>>>>>> > operator-(const _Iterator& __i, difference_type __n)
>>>>>> > requires random_access_range<_Base>
>>>>>> > - { return _Iterator(*__i._M_parent, __i._M_inner - __n); }
>>>>>> > + { return _Iterator(__i._M_fun, __i._M_inner - __n); }
>>>>>> >
>>>>>> > friend constexpr difference_type
>>>>>> > operator-(const _Iterator& __x, const _Iterator& __y)
>>>>>> > diff --git
>>>>>> a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc
>>>>>> b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc
>>>>>> > index 772e4b3b6a0..1667c6250c5 100644
>>>>>> > ---
>>>>>> a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc
>>>>>> > +++
>>>>>> b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc
>>>>>> > @@ -113,6 +113,97 @@ test04()
>>>>>> > static_assert( requires { x |
>>>>>> views::pairwise_transform(move_only{}); } );
>>>>>> > }
>>>>>> >
>>>>>> > +struct X
>>>>>> > +{
>>>>>> > + int i,j;
>>>>>> > + constexpr int combine(X o) const
>>>>>> > + { return i + o.j; }
>>>>>> > +};
>>>>>> > +
>>>>>> > +template<size_t FuncSize, typename Fn>
>>>>>> > +constexpr bool
>>>>>> > +test05(Fn f)
>>>>>> > +{
>>>>>> > + using namespace __gnu_test;
>>>>>> > + X x[] = {{1,500},{2,400},{3,300},{4,200},{5,100}};
>>>>>> > + test_range<X, random_access_iterator_wrapper> rx(x);
>>>>>> > +
>>>>>> > + auto v = rx | views::pairwise_transform(f);
>>>>>> > + VERIFY( ranges::size(v) == 4 );
>>>>>> > + VERIFY( ranges::distance(v.begin(), v.end()) == 4 );
>>>>>> > + VERIFY( ranges::equal(v, (int[]){401,302,203,104}) );
>>>>>> > + VERIFY( ranges::equal(v | views::reverse,
>>>>>> (int[]){104,203,302,401}) );
>>>>>> > + using R = decltype(v);
>>>>>> > + using It = ranges::iterator_t<R>;
>>>>>> > + static_assert(std::same_as<int, decltype(*ranges::begin(v))>);
>>>>>> > + static_assert(std::same_as<int, std::iter_value_t<It>>);
>>>>>> > + static_assert(sizeof(It) == 2*sizeof(rx.begin()) + FuncSize);
>>>>>> > + static_assert(ranges::view<R>);
>>>>>> > + static_assert(ranges::sized_range<R>);
>>>>>> > + static_assert(!ranges::common_range<R>);
>>>>>> > + static_assert(ranges::random_access_range<R>);
>>>>>> > + return true;
>>>>>> > +}
>>>>>> > +
>>>>>> > +constexpr bool
>>>>>> > +test05a()
>>>>>> > +{
>>>>>> > + auto comb = [](const X& x1, const X& x2) { return x1.i + x2.j; };
>>>>>> > + return test05<sizeof(void*)>(comb);
>>>>>> > +}
>>>>>> > +
>>>>>> > +constexpr bool
>>>>>> > +test05b()
>>>>>> > +{
>>>>>> > + auto comb = [](const X& x1, const X& x2) static { return x1.i +
>>>>>> x2.j; };
>>>>>> > + return test05<0>(comb);
>>>>>> > +}
>>>>>> > +
>>>>>> > +constexpr bool
>>>>>> > +test05c()
>>>>>> > +{
>>>>>> > + int(*comb)(const X&, const X&) = [](const X& x1, const X& x2) {
>>>>>> return x1.i + x2.j; };
>>>>>> > + return test05<sizeof(void(*)())>(comb);
>>>>>> > +}
>>>>>> > +
>>>>>> > +constexpr bool
>>>>>> > +test05d()
>>>>>> > +{
>>>>>> > + return test05<sizeof(int(X::*)())>(&X::combine);
>>>>>> > +}
>>>>>> > +
>>>>>> > +constexpr bool
>>>>>> > +test05e()
>>>>>> > +{
>>>>>> > + struct PickStatic
>>>>>> > + {
>>>>>> > + static constexpr int
>>>>>> > + operator()(const X& x1, const X& x2)
>>>>>> > + { return x1.i + x2.j; }
>>>>>> > +
>>>>>> > + constexpr int
>>>>>> > + operator()(int x, int y) const
>>>>>> > + { return x + y; };
>>>>>> > + };
>>>>>> > + return test05<0>(PickStatic{});
>>>>>> > +}
>>>>>> > +
>>>>>> > +constexpr bool
>>>>>> > +test05f()
>>>>>> > +{
>>>>>> > + struct PickObject
>>>>>> > + {
>>>>>> > + constexpr int
>>>>>> > + operator()(const X& x1, const X& x2) const
>>>>>> > + { return x1.i + x2.j; }
>>>>>> > +
>>>>>> > + static constexpr int
>>>>>> > + operator()(int x, int y)
>>>>>> > + { return x + y; };
>>>>>> > + };
>>>>>> > + return test05<sizeof(void*)>(PickObject{});
>>>>>> > +}
>>>>>> > +
>>>>>> > int
>>>>>> > main()
>>>>>> > {
>>>>>> > @@ -120,4 +211,10 @@ main()
>>>>>> > static_assert(test02());
>>>>>> > static_assert(test03());
>>>>>> > test04();
>>>>>> > + static_assert(test05a());
>>>>>> > + static_assert(test05b());
>>>>>> > + static_assert(test05c());
>>>>>> > + static_assert(test05d());
>>>>>> > + static_assert(test05e());
>>>>>> > + static_assert(test05f());
>>>>>> > }
>>>>>> > diff --git
>>>>>> a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
>>>>>> b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
>>>>>> > index 1788db1ce8d..c6d6b916730 100644
>>>>>> > --- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
>>>>>> > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
>>>>>> > @@ -28,12 +28,12 @@ using
>>>>>> __gnu_test::random_access_iterator_wrapper;
>>>>>> > namespace ranges = std::ranges;
>>>>>> > namespace views = std::ranges::views;
>>>>>> >
>>>>>> > +template<typename Fn>
>>>>>> > void
>>>>>> > -test01()
>>>>>> > +test01(Fn f)
>>>>>> > {
>>>>>> > int x[] = {1,2,3,4,5};
>>>>>> > - auto is_odd = [] (int i) { return i%2==1; };
>>>>>> > - auto v = x | views::transform(is_odd);
>>>>>> > + auto v = x | views::transform(f);
>>>>>> > VERIFY( ranges::equal(v, (int[]){1,0,1,0,1}) );
>>>>>> > using R = decltype(v);
>>>>>> > static_assert(std::same_as<bool, decltype(*ranges::begin(v))>);
>>>>>> > @@ -42,30 +42,124 @@ test01()
>>>>>> > static_assert(ranges::random_access_range<R>);
>>>>>> > }
>>>>>> >
>>>>>> > +void
>>>>>> > +test01a()
>>>>>> > +{
>>>>>> > + auto is_odd = [] (int i) { return i%2==1; };
>>>>>> > + test01(is_odd);
>>>>>> > +}
>>>>>> > +
>>>>>> > +void
>>>>>> > +test01b()
>>>>>> > +{
>>>>>> > +#if __cpp_static_call_operator >= 202207L
>>>>>> > + auto is_odd = [] (int i) static { return i%2==1; };
>>>>>> > + test01(is_odd);
>>>>>> > +#endif
>>>>>> > +}
>>>>>> > +
>>>>>> > +void
>>>>>> > +test01c()
>>>>>> > +{
>>>>>> > + bool(*is_odd)(int) = [] (int i) { return i%2==1; };
>>>>>> > + test01(is_odd);
>>>>>> > +}
>>>>>> > +
>>>>>> > struct X
>>>>>> > {
>>>>>> > int i,j;
>>>>>> > + int& first() { return i; }
>>>>>> > };
>>>>>> >
>>>>>> > +template<size_t FuncSize, typename Fn>
>>>>>> > void
>>>>>> > -test02()
>>>>>> > +test02(Fn f)
>>>>>> > {
>>>>>> > X x[] = {{1,2},{3,4},{5,6},{7,8},{9,10}};
>>>>>> > test_range<X, random_access_iterator_wrapper> rx(x);
>>>>>> > - auto v = rx | views::transform(&X::i);
>>>>>> > + auto v = rx | views::transform(f);
>>>>>> > VERIFY( ranges::size(v) == 5 );
>>>>>> > VERIFY( ranges::distance(v.begin(), v.end()) == 5 );
>>>>>> > VERIFY( ranges::equal(v, (int[]){1,3,5,7,9}) );
>>>>>> > VERIFY( ranges::equal(v | views::reverse, (int[]){9,7,5,3,1}) );
>>>>>> > using R = decltype(v);
>>>>>> > + using It = ranges::iterator_t<R>;
>>>>>> > static_assert(std::same_as<int&, decltype(*ranges::begin(v))>);
>>>>>> > - static_assert(std::same_as<int,
>>>>>> std::iter_value_t<ranges::iterator_t<R>>>);
>>>>>> > + static_assert(std::same_as<int, std::iter_value_t<It>>);
>>>>>> > + static_assert(sizeof(It) == sizeof(rx.begin()) + FuncSize);
>>>>>> > static_assert(ranges::view<R>);
>>>>>> > static_assert(ranges::sized_range<R>);
>>>>>> > static_assert(!ranges::common_range<R>);
>>>>>> > static_assert(ranges::random_access_range<R>);
>>>>>> > }
>>>>>> >
>>>>>> > +void
>>>>>> > +test02a()
>>>>>> > +{ test02<sizeof(int X::*)>(&X::i); }
>>>>>> > +
>>>>>> > +void
>>>>>> > +test02b()
>>>>>> > +{ test02<sizeof(int(X::*)())>(&X::first); }
>>>>>> > +
>>>>>> > +void
>>>>>> > +test02c()
>>>>>> > +{
>>>>>> > + auto first = [](X& x) -> int& { return x.i; };
>>>>>> > + test02<sizeof(void*)>(first);
>>>>>> > +}
>>>>>> > +
>>>>>> > +void
>>>>>> > +test02d()
>>>>>> > +{
>>>>>> > +#if __cpp_static_call_operator >= 202207L
>>>>>> > + auto first = [](X& x) static -> int& { return x.i; };
>>>>>> > + test02<0>(first);
>>>>>> > +#endif
>>>>>> > +}
>>>>>> > +
>>>>>> > +void
>>>>>> > +test02e()
>>>>>> > +{
>>>>>> > + int&(*fptr)(X&) = [](X& x) -> int& { return x.i; };
>>>>>> > + test02<sizeof(void(*)())>(fptr);
>>>>>> > +}
>>>>>> > +
>>>>>> > +void
>>>>>> > +test02f()
>>>>>> > +{
>>>>>> > +#if __cpp_static_call_operator >= 202207L
>>>>>> > + struct PickStatic
>>>>>> > + {
>>>>>> > + static constexpr int&
>>>>>> > + operator()(X& x)
>>>>>> > + { return x.i; }
>>>>>> > +
>>>>>> > + constexpr int
>>>>>> > + operator()(char*) const
>>>>>> > + { return 0; };
>>>>>> > + };
>>>>>> > + test02<0>(PickStatic{});
>>>>>> > +#endif
>>>>>> > +}
>>>>>> > +
>>>>>> > +void
>>>>>> > +test02g()
>>>>>> > +{
>>>>>> > +#if __cpp_static_call_operator >= 202207L
>>>>>> > + struct PickObject
>>>>>> > + {
>>>>>> > + constexpr int&
>>>>>> > + operator()(X& x) const
>>>>>> > + { return x.i; }
>>>>>> > +
>>>>>> > + static constexpr int
>>>>>> > + operator()(char*)
>>>>>> > + { return 0; };
>>>>>> > + };
>>>>>> > + test02<sizeof(void*)>(PickObject{});
>>>>>> > +#endif
>>>>>> > +}
>>>>>> > +
>>>>>> > void
>>>>>> > test03()
>>>>>> > {
>>>>>> > @@ -230,8 +324,16 @@ test11()
>>>>>> > int
>>>>>> > main()
>>>>>> > {
>>>>>> > - test01();
>>>>>> > - test02();
>>>>>> > + test01a();
>>>>>> > + test01b();
>>>>>> > + test01c();
>>>>>> > + test02a();
>>>>>> > + test02b();
>>>>>> > + test02c();
>>>>>> > + test02d();
>>>>>> > + test02e();
>>>>>> > + test02f();
>>>>>> > + test02g();
>>>>>> > test03();
>>>>>> > test04();
>>>>>> > test05();
>>>>>> > diff --git a/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
>>>>>> b/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
>>>>>> > index 9a0ad3814e6..d4bd7db26cb 100644
>>>>>> > --- a/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
>>>>>> > +++ b/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
>>>>>> > @@ -132,6 +132,97 @@ test04()
>>>>>> > static_assert( requires { views::zip_transform(move_only{}, x,
>>>>>> x); } );
>>>>>> > }
>>>>>> >
>>>>>> > +struct X
>>>>>> > +{
>>>>>> > + int i;
>>>>>> > + constexpr int add(int b) const
>>>>>> > + { return i+b; }
>>>>>> > +};
>>>>>> > +
>>>>>> > +template<size_t ExtraSize, typename Fn>
>>>>>> > +constexpr bool
>>>>>> > +test05(Fn f)
>>>>>> > +{
>>>>>> > + using namespace __gnu_test;
>>>>>> > + X x[] = {{1},{2},{3},{4},{5}};
>>>>>> > + int y[] = {500,400,300,200,100};
>>>>>> > + test_range<X, random_access_iterator_wrapper> rx(x);
>>>>>> > + test_range<int, random_access_iterator_wrapper> ry(y);
>>>>>> > +
>>>>>> > + auto v = views::zip_transform(f, rx, ry);
>>>>>> > + VERIFY( ranges::size(v) == 5 );
>>>>>> > + VERIFY( ranges::distance(v.begin(), v.end()) == 5 );
>>>>>> > + VERIFY( ranges::equal(v, (int[]){501,402,303,204,105}) );
>>>>>> > + VERIFY( ranges::equal(v | views::reverse,
>>>>>> (int[]){105,204,303,402,501}) );
>>>>>> > + using R = decltype(v);
>>>>>> > + using It = ranges::iterator_t<R>;
>>>>>> > + static_assert(std::same_as<int, decltype(*ranges::begin(v))>);
>>>>>> > + static_assert(std::same_as<int, std::iter_value_t<It>>);
>>>>>> > + static_assert(sizeof(It) == sizeof(rx.begin()) +
>>>>>> sizeof(ry.begin()) + ExtraSize);
>>>>>> > + static_assert(ranges::view<R>);
>>>>>> > + static_assert(ranges::sized_range<R>);
>>>>>> > + static_assert(ranges::common_range<R>);
>>>>>> > + static_assert(ranges::random_access_range<R>);
>>>>>> > + return true;
>>>>>> > +}
>>>>>> > +
>>>>>> > +constexpr bool
>>>>>> > +test05a()
>>>>>> > +{
>>>>>> > + auto add = [](const X& x, int v) { return x.i + v; };
>>>>>> > + return test05<sizeof(void*)>(add);
>>>>>> > +}
>>>>>> > +
>>>>>> > +constexpr bool
>>>>>> > +test05b()
>>>>>> > +{
>>>>>> > + auto add = [](const X& x, int v) static { return x.i + v; };
>>>>>> > + return test05<0>(add);
>>>>>> > +}
>>>>>> > +
>>>>>> > +constexpr bool
>>>>>> > +test05c()
>>>>>> > +{
>>>>>> > + int(*ptr)(const X&, int) = [](const X& x, int v) { return x.i +
>>>>>> v; };
>>>>>> > + return test05<sizeof(void(*)())>(ptr);
>>>>>> > +}
>>>>>> > +
>>>>>> > +constexpr bool
>>>>>> > +test05d()
>>>>>> > +{ return test05<sizeof(int(X::*)())>(&X::add); }
>>>>>> > +
>>>>>> > +constexpr bool
>>>>>> > +test05e()
>>>>>> > +{
>>>>>> > + struct PickStatic
>>>>>> > + {
>>>>>> > + static constexpr int
>>>>>> > + operator()(const X& x1, int v)
>>>>>> > + { return x1.i + v; }
>>>>>> > +
>>>>>> > + constexpr int
>>>>>> > + operator()(int x, int y) const
>>>>>> > + { return x + y; };
>>>>>> > + };
>>>>>> > + return test05<0>(PickStatic{});
>>>>>> > +}
>>>>>> > +
>>>>>> > +constexpr bool
>>>>>> > +test05f()
>>>>>> > +{
>>>>>> > + struct PickObject
>>>>>> > + {
>>>>>> > + constexpr int
>>>>>> > + operator()(const X& x1, int v) const
>>>>>> > + { return x1.i + v; }
>>>>>> > +
>>>>>> > + static constexpr int
>>>>>> > + operator()(int x, int y)
>>>>>> > + { return x + y; };
>>>>>> > + };
>>>>>> > + return test05<sizeof(void*)>(PickObject{});
>>>>>> > +}
>>>>>> > +
>>>>>> > int
>>>>>> > main()
>>>>>> > {
>>>>>> > @@ -139,4 +230,14 @@ main()
>>>>>> > static_assert(test02());
>>>>>> > static_assert(test03());
>>>>>> > test04();
>>>>>> > + static_assert(test01());
>>>>>> > + static_assert(test02());
>>>>>> > + static_assert(test03());
>>>>>> > + test04();
>>>>>> > + static_assert(test05a());
>>>>>> > + static_assert(test05b());
>>>>>> > + static_assert(test05c());
>>>>>> > + static_assert(test05d());
>>>>>> > + static_assert(test05e());
>>>>>> > + static_assert(test05f());
>>>>>> > }
>>>>>> > --
>>>>>> > 2.51.0
>>>>>> >
>>>>>> >
>>>>>
>>>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20251114/989ad5cc/attachment-0001.htm>
More information about the Libstdc++
mailing list