On Fri, Nov 14, 2025 at 10:17 AM Hewill Kang <[email protected]> wrote:

> +    constexpr
> +    _Iterator(_Fun_handle __fun, __ziperator<_Const> __inner)
> +      : _M_fun(__fun), _M_inner(std::move(__inner))
> +    { }
>
> Is it reasonable for these implemented-detailed constructors to be made
> private? The rest is good to me.
>
I am not sure, certainly do not want to bundle it with the changes in this
patch.

> Thank you for the contribution!
>
>
> Tomasz Kamiński <[email protected]> 於 2025年11月14日 週五 下午4:28寫道:
>
>> 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.
>> * _Stateful: 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 _Stateful. 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.
>>
>> Reviewed-by: Patrick Palka <[email protected]>
>> Signed-off-by: Tomasz Kamiński <[email protected]>
>> ---
>> v2:
>>  - rename _Statefull to _Stateful
>>  - make __func_handle<T> to __func_handle<const T> implicit, as
>>    suggested by Hewill
>>  - use less criptic (_Ids, _Const) when construction _Fun_handle
>>    for adjacent_transform
>>  - add test for empty functor that depends on *this
>>  - add test for overload resolution selecting between static and
>>    member operators
>>
>> Tested on x86_64-linux locally. OK for trunk?
>>
>>
>>  libstdc++-v3/include/std/ranges               | 214 +++++++++++++++---
>>  .../ranges/adaptors/adjacent_transform/1.cc   |  97 ++++++++
>>  .../std/ranges/adaptors/transform.cc          | 160 ++++++++++++-
>>  .../testsuite/std/ranges/zip_transform/1.cc   | 101 +++++++++
>>  4 files changed, 535 insertions(+), 37 deletions(-)
>>
>> diff --git a/libstdc++-v3/include/std/ranges
>> b/libstdc++-v3/include/std/ranges
>> index ae57b9a0809..e4851df804f 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,
>> +       _Stateful,
>> +      };
>> +
>> +      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
>> +         __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::_Stateful>();
>> +       }());
>>    } // 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<(_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 +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..b0313130e26 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,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()
>>  {
>> @@ -227,11 +322,58 @@ 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
>> +}
>> +
>>  int
>>  main()
>>  {
>> -  test01();
>> -  test02();
>> +  test01a();
>> +  test01b();
>> +  test01c();
>> +  test02a();
>> +  test02b();
>> +  test02c();
>> +  test02d();
>> +  test02e();
>> +  test02f();
>> +  test02g();
>>    test03();
>>    test04();
>>    test05();
>> @@ -241,4 +383,6 @@ main()
>>    test09();
>>    test10();
>>    test11();
>> +  test12();
>> +  test13();
>>  }
>> 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