On Thu, 20 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 
> _FuncHandleType
> template parameter:
> * _Inplace: Used if the functor is a function pointer or standard  operator
>   wrapper (std::less<>, etc). The functor is  stored directly in __func_handle
>   and the iterator. This avoid double indirection through a pointer to the
>   function pointer, and reduce the size of iterator for std wrappers.
> * _InplaceMemPtr: Used for data or function member pointers. This behaves
>   similarly to _Inplace, but uses __invoke for invocations.
> * _StaticCall: 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.
> * _ViaPointer: 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.
> 
> 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 _ViaPointer. The corresponding views are not marked as borrowed_range,
> as they are not marked as such in the standard.
> 
> The use of _Inplace is limited to only set of standard functors, as arbitrary
> empty functor may still depend on value of this pointer in their operator(),
> as illustrated by test12 in std/ranges/adaptors/transform.cc test file.
> 
> 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/bits/utility.h (__is_specialization_of): Moved from
>       std/format.
>       * include/std/format (__is_specialization_of): Moved to
>       bits/utility.
>       * include/std/ranges (__detail::__is_std_op_wrapper)
>       (__detail::_FuncHandleType, __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.
> 
> Signed-off-by: Tomasz Kamiński <[email protected]>
> ---
> v2:
> - stores inside of iterator selected set of standard operators
> - renames the _FunctorType enum to _FuncHandleEnum and corresponding
>   enumertor values (_Inplace is also used for empty functors)
> - do not define __func_handle primary template, and provide separate
>   specialization for _ViaTemplate (previously _Stateful)
> - add test for all functors
> 
> Tessted on x86_64-linux linux. *range*transform* additionally tested
> with all standard modes.
> 
>  libstdc++-v3/include/bits/utility.h           |   9 +
>  libstdc++-v3/include/std/format               |   5 -
>  libstdc++-v3/include/std/ranges               | 249 ++++++++++++++++--
>  .../ranges/adaptors/adjacent_transform/1.cc   |  41 +++
>  .../std/ranges/adaptors/transform.cc          | 185 ++++++++++++-
>  .../testsuite/std/ranges/zip_transform/1.cc   | 101 +++++++
>  6 files changed, 548 insertions(+), 42 deletions(-)
> 
> diff --git a/libstdc++-v3/include/bits/utility.h 
> b/libstdc++-v3/include/bits/utility.h
> index 96ac69883f1..522a7a1d884 100644
> --- a/libstdc++-v3/include/bits/utility.h
> +++ b/libstdc++-v3/include/bits/utility.h
> @@ -46,6 +46,15 @@ namespace std _GLIBCXX_VISIBILITY(default)
>  {
>  _GLIBCXX_BEGIN_NAMESPACE_VERSION
>  
> +/// @cond undocumented
> +#if __cplusplus >= 201703L
> +  template<typename _Tp, template<typename...> class _Class>
> +    constexpr bool __is_specialization_of = false;
> +  template<template<typename...> class _Class, typename... _Args>
> +    constexpr bool __is_specialization_of<_Class<_Args...>, _Class> = true;
> +#endif
> +/// @endcond
> +
>    /// Finds the size of a given tuple type.
>    template<typename _Tp>
>      struct tuple_size;
> diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
> index f64f35a202e..1e1ec1fb98f 100644
> --- a/libstdc++-v3/include/std/format
> +++ b/libstdc++-v3/include/std/format
> @@ -417,11 +417,6 @@ namespace __format
>      };
>  
>  /// @cond undocumented
> -  template<typename _Tp, template<typename...> class _Class>
> -    constexpr bool __is_specialization_of = false;
> -  template<template<typename...> class _Class, typename... _Args>
> -    constexpr bool __is_specialization_of<_Class<_Args...>, _Class> = true;
> -
>  namespace __format
>  {
>    // pre: first != last
> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index ae57b9a0809..b384e5e45d3 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -286,6 +286,173 @@ namespace ranges
>       operator->() const noexcept
>       { return std::__addressof(_M_value); }
>        };
> +
> +      enum class _FuncHandleType
> +      {
> +     _Inplace,
> +     _InplaceMemPtr,
> +     _ViaPointer,
> +     _StaticCall,
> +      };
> +
> +      template<typename _Fn>
> +     concept __is_std_op_wrapper = is_empty_v<_Fn> && (
> +       is_same_v<_Fn, std::identity>
> +       || __is_specialization_of<_Fn, std::equal_to>
> +       || __is_specialization_of<_Fn, std::not_equal_to>
> +       || __is_specialization_of<_Fn, std::greater>
> +       || __is_specialization_of<_Fn, std::less>
> +       || __is_specialization_of<_Fn, std::greater_equal>
> +       || __is_specialization_of<_Fn, std::less_equal>
> +       || is_same_v<_Fn, std::compare_three_way>
> +       || is_same_v<_Fn, std::ranges::equal_to>
> +       || is_same_v<_Fn, std::ranges::not_equal_to>
> +       || is_same_v<_Fn, std::ranges::greater>
> +       || is_same_v<_Fn, std::ranges::less>
> +       || is_same_v<_Fn, std::ranges::greater_equal>
> +       || is_same_v<_Fn, std::ranges::less_equal>
> +       || __is_specialization_of<_Fn, std::plus>
> +       || __is_specialization_of<_Fn, std::minus>
> +       || __is_specialization_of<_Fn, std::multiplies>
> +       || __is_specialization_of<_Fn, std::divides>
> +       || __is_specialization_of<_Fn, std::modulus>
> +       || __is_specialization_of<_Fn, std::negate>
> +       || __is_specialization_of<_Fn, std::logical_and>
> +       || __is_specialization_of<_Fn, std::logical_or>
> +       || __is_specialization_of<_Fn, std::logical_not>
> +       || __is_specialization_of<_Fn, std::bit_and>
> +       || __is_specialization_of<_Fn, std::bit_or>
> +       || __is_specialization_of<_Fn, std::bit_xor>
> +       || __is_specialization_of<_Fn, std::bit_not>);

While the use of __is_specialization_of is nice and clean here, it means
the concept is effectively O(n) in the number of standard functors we're
accepting, which for n=27 seems too high.

Rather than using __is_specialization_of we can define this concept
in terms of two variable templates, one for the non-template functors
and one for the template functors, and enumerate the set of allowed
standard functors via explicit specializations.  Explicit
specializations are O(1) to resolve, making the overall concept O(1).

> +
> +      template<typename _Fn, _FuncHandleType __ft>
> +     struct __func_handle;
> +
> +      template<typename _Fn>
> +     struct __func_handle<_Fn, _FuncHandleType::_Inplace>
> +     {
> +       __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:
> +       [[no_unique_address]] _Fn _M_ptr = _Fn();
> +     };
> +
> +      template<typename _Fn>
> +     struct __func_handle<_Fn, _FuncHandleType::_InplaceMemPtr>
> +     {
> +       __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, _FuncHandleType::_ViaPointer>
> +     {
> +       __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
> +         __func_handle(__func_handle<_Un, _FuncHandleType::_ViaPointer> 
> __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, _FuncHandleType>
> +         friend struct __func_handle;
> +     };
> +
> +     template<typename _Fn>
> +     struct __func_handle<_Fn, _FuncHandleType::_StaticCall>
> +     {
> +       __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)]...); }
> +     };

Partial specializations are O(n) to resolve, but here n is just 4 which
isn't so bad.  But if you want we can make this O(1) as well by making
each partial specialization its own distinct class template.

> +
> +      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, _FuncHandleType::_InplaceMemPtr>();
> +       else if constexpr (is_function_v<remove_pointer_t<_Fd>>)
> +         return __func_handle<_Fd, _FuncHandleType::_Inplace>();
> +       else if constexpr (__is_std_op_wrapper<_Fd>)
> +         return __func_handle<_Fd, _FuncHandleType::_Inplace>();
> +       else if constexpr (requires (const _Iters&... __iters)
> +                            { _Fd::operator()(*__iters...); })
> +         return __func_handle<_Fd, _FuncHandleType::_StaticCall>();
> +       else
> +         return __func_handle<_Fn, _FuncHandleType::_ViaPointer>();
> +     }());
>    } // namespace __detail
>  
>    /// A view that contains exactly one element.
> @@ -1874,6 +2041,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 +2059,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 +2073,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 +2099,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 +2153,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 +2191,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 +5299,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 +5331,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 +5394,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 +5411,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 +5988,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<(_Ids, 
> _Const), _Vp>>...>();
> +                     }(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 +6045,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 +6104,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 +6141,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..6890618754b 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,47 @@ test04()
>    static_assert( requires { x | views::pairwise_transform(move_only{}); } );
>  }
>  
> +template<size_t FuncSize, typename Fn>
> +void
> +test05(Fn f)
> +{
> +  int x[] = {1,2,3,4,5,6};
> +  auto v = x | views::pairwise_transform(f);
> +  static_assert(sizeof(v.begin()) == 2*sizeof(int*) + FuncSize);
> +}
> +
> +void
> +test05all()
> +{
> +  test05<0>(std::equal_to<>());
> +  test05<0>(std::equal_to<>());
> +  test05<0>(std::not_equal_to<>());
> +  test05<0>(std::greater<>());
> +  test05<0>(std::less<>());
> +  test05<0>(std::greater_equal<>());
> +  test05<0>(std::less_equal<>());
> +
> +  test05<0>(std::ranges::equal_to());
> +  test05<0>(std::ranges::not_equal_to());
> +  test05<0>(std::ranges::greater());
> +  test05<0>(std::ranges::less());
> +  test05<0>(std::ranges::greater_equal());
> +  test05<0>(std::ranges::less_equal());
> +
> +  test05<0>(std::plus<>());
> +  test05<0>(std::minus<>());
> +  test05<0>(std::multiplies<>());
> +  test05<0>(std::divides<>());
> +  test05<0>(std::modulus<>());
> +
> +  test05<0>(std::logical_and<>());
> +  test05<0>(std::logical_or<>());
> +
> +  test05<0>(std::bit_and<>());
> +  test05<0>(std::bit_or<>());
> +  test05<0>(std::bit_xor<>());
> +}
> +
>  int
>  main()
>  {
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc 
> b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
> index 1788db1ce8d..7a0c344cc5c 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
> @@ -18,6 +18,7 @@
>  // { dg-do run { target c++20 } }
>  
>  #include <algorithm>
> +#include <cstdint>
>  #include <ranges>
>  #include <testsuite_hooks.h>
>  #include <testsuite_iterators.h>
> @@ -28,12 +29,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 +43,131 @@ 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
> +test02h()
> +{ 
> +  int&(*fptr)(X&) = [](X& x) -> int& { return x.i; };
> +  test02<sizeof(void(*)())>(fptr);
> +}

Identical to test02e?

> +
>  void
>  test03()
>  {
> @@ -227,11 +329,75 @@ test11()
>    static_assert(std::same_as<cat, std::random_access_iterator_tag>);
>  }
>  
> +void
> +test12()
> +{
> +  struct Obfuscate
> +  {
> +    int operator()(int x) const
> +    { return x + reinterpret_cast<std::uintptr_t>(this); }
> +  };
> +
> +  int x[]{1, 2, 3, 4, 5};
> +  auto v = x | views::transform(Obfuscate{});
> +  VERIFY( ranges::equal(v, v) );
> +};
> +
> +void
> +test13()
> +{ 
> +#if __cpp_static_call_operator >= 202207L
> +  struct StaticWins {
> +    static int operator()(int i) { return 0; }
> +    int operator()(float f) const { return 1; }
> +  };
> +
> +  int x[]{1, 2, 3, 4, 5};
> +  auto vs = x | views::transform(StaticWins{});
> +  VERIFY( vs.front() == 0 );
> +  static_assert( sizeof(vs.begin()) == sizeof(int*) );
> +
> +  struct MemberWins {
> +    static int operator()(float f) { return 0; }
> +    int operator()(int i) const { return 1; }
> +  };
> +
> +  auto vm = x | views::transform(MemberWins{});
> +  VERIFY( vm.front() == 1 );
> +  static_assert( sizeof(vm.begin()) > sizeof(int*) );
> +#endif
> +}
> +
> +template<size_t FuncSize, typename Fn>
> +void
> +test14(Fn f)
> +{
> +  int x[] = {1,2,3,4,5,6};
> +  auto v = x | views::transform(std::negate<>());
> +  static_assert(sizeof(v.begin()) == sizeof(int*) + FuncSize);
> +}
> +
> +void
> +test14all()
> +{
> +  test14<0>(std::identity());
> +  test14<0>(std::negate<>());
> +  test14<0>(std::bit_not<>());
> +}
> +
>  int
>  main()
>  {
> -  test01();
> -  test02();
> +  test01a();
> +  test01b();
> +  test01c();
> +  test02a();
> +  test02b();
> +  test02c();
> +  test02d();
> +  test02e();
> +  test02f();
> +  test02g();
>    test03();
>    test04();
>    test05();
> @@ -241,4 +407,7 @@ main()
>    test09();
>    test10();
>    test11();
> +  test12();
> +  test13();
> +  test14all();
>  }
> 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.1
> 
> 

Reply via email to