On Fri, Nov 14, 2025 at 2:18 PM Hewill Kang <[email protected]> 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 <[email protected]> 於 2025年11月14日 週五 下午5:36寫道:
>
>>
>>
>> On Fri, Nov 14, 2025 at 10:24 AM Hewill Kang <[email protected]> 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 <[email protected]> 於 2025年11月14日 週五 下午2:36寫道:
>>>
>>>>
>>>>
>>>> On Fri, Nov 14, 2025 at 4:16 AM Patrick Palka <[email protected]>
>>>> 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
>>>>> >
>>>>> >
>>>>
>>>>

Reply via email to