https://gcc.gnu.org/g:1a41e52d7ecb583947a37b8d2e16a0a9ea3777a4

commit r16-3575-g1a41e52d7ecb583947a37b8d2e16a0a9ea3777a4
Author: Tomasz Kamiński <[email protected]>
Date:   Mon Sep 1 14:37:57 2025 +0200

    libstdc++: Reuse _Bind_back_t functor in ranges::_Partial
    
    This patch refactors ranges::_Partial to be implemented using _Bind_back_t.
    This allows it to benefit from the changes in r16-3398-g250dd5b5604fbc,
    specifically making the closure trivially copyable. Since _Bind_back_t
    already provides an optimized implementation for a single bound argument,
    specializations for _Partial with a single argument are now removed.
    
    We still preserve a specialization of _Partial for trivially 
copy-constructible
    arguments that define only a const overload of operator(). To avoid
    re-checking invocability constraints, this specialization calls the 
now-public,
    unconstrained _Binder::_S_call static method instead of the constrained
    _Binder::operator().
    
    The primary specialization of _Partial retains its operator(), which
    uses a simpler __adaptor_invocable constraint that does not consider
    member pointers, as they are not relevant here. This implementation also
    calls _Binder::_S_call to avoid re-performing overload resolution and
    invocability checks for _Binder::operator().
    
    Finally, the _M_binder member (_Bind_back_t) is now marked
    [[no_unique_address]]. This is beneficial as ranges::_Partial is used with
    ranges::to, which commonly has zero or empty bound arguments (e.g., 
stateless
    allocators, comparators, or hash functions).
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/binders.h (_Binder::_S_call): Make public.
            * include/std/ranges (ranges::_Partial<_Adaptor, _Args...>):
            Replace tuple<_Args...> with _Bind_back_t<_Adaptor, _Args...>.
            (ranges::_Partial<_Adaptor, _Arg>): Remove.
    
    Reviewed-by: Patrick Palka <[email protected]>
    Reviewed-by: Jonathan Wakely <[email protected]>
    Signed-off-by: Tomasz Kamiński <[email protected]>

Diff:
---
 libstdc++-v3/include/bits/binders.h |  10 ++--
 libstdc++-v3/include/std/ranges     | 104 +++++-------------------------------
 2 files changed, 17 insertions(+), 97 deletions(-)

diff --git a/libstdc++-v3/include/bits/binders.h 
b/libstdc++-v3/include/bits/binders.h
index c324bf51481c..6489edd5321b 100644
--- a/libstdc++-v3/include/bits/binders.h
+++ b/libstdc++-v3/include/bits/binders.h
@@ -186,11 +186,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        void operator()(_CallArgs&&...) const && = delete;
 #endif
 
-    private:
-      using _BoundArgsStorage
-       // _BoundArgs are required to be move-constructible, so this is valid.
-       = 
decltype(__make_bound_args<_BoundArgs...>(std::declval<_BoundArgs>()...));
-
       template<typename _Tp, typename... _CallArgs>
        static constexpr
        decltype(auto)
@@ -214,6 +209,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                                 std::forward<_CallArgs>(__call_args)...);
        }
 
+    private:
+      using _BoundArgsStorage
+       // _BoundArgs are required to be move-constructible, so this is valid.
+       = 
decltype(__make_bound_args<_BoundArgs...>(std::declval<_BoundArgs>()...));
+
       [[no_unique_address]] _Fd _M_fd;
       [[no_unique_address]] _BoundArgsStorage _M_bound_args;
     };
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 2970b2cbe00b..156d12b31ba1 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -51,6 +51,7 @@
 #include <utility>
 #include <variant>
 #endif
+#include <bits/binders.h>
 #include <bits/ranges_util.h>
 #include <bits/refwrap.h>
 
@@ -1047,14 +1048,15 @@ namespace views::__adaptor
   template<typename _Adaptor, typename... _Args>
     struct _Partial : _RangeAdaptorClosure<_Partial<_Adaptor, _Args...>>
     {
-      tuple<_Args...> _M_args;
+      using _Binder = _Bind_back_t<_Adaptor, _Args...>;
+      [[no_unique_address]] _Binder _M_binder;
 
       // First parameter is to ensure this constructor is never used
       // instead of the copy/move constructor.
       template<typename... _Ts>
        constexpr
        _Partial(int, _Ts&&... __args)
-         : _M_args(std::forward<_Ts>(__args)...)
+         : _M_binder(0, _Adaptor(), std::forward<_Ts>(__args)...)
        { }
 
       // Invoke _Adaptor with arguments __r, _M_args... according to the
@@ -1065,75 +1067,21 @@ namespace views::__adaptor
        constexpr auto
        operator()(this _Self&& __self, _Range&& __r)
        {
-         auto __forwarder = [&__r] (auto&&... __args) {
-           return _Adaptor{}(std::forward<_Range>(__r),
-                             std::forward<decltype(__args)>(__args)...);
-         };
-         return std::apply(__forwarder, __like_t<_Self, 
_Partial>(__self)._M_args);
+         return _Binder::_S_call(__like_t<_Self, _Partial>(__self)._M_binder,
+                                 std::forward<_Range>(__r));
        }
 #else
       template<typename _Range>
        requires __adaptor_invocable<_Adaptor, _Range, const _Args&...>
        constexpr auto
        operator()(_Range&& __r) const &
-       {
-         auto __forwarder = [&__r] (const auto&... __args) {
-           return _Adaptor{}(std::forward<_Range>(__r), __args...);
-         };
-         return std::apply(__forwarder, _M_args);
-       }
+       { return _Binder::_S_call(_M_binder, std::forward<_Range>(__r)); }
 
       template<typename _Range>
        requires __adaptor_invocable<_Adaptor, _Range, _Args...>
        constexpr auto
        operator()(_Range&& __r) &&
-       {
-         auto __forwarder = [&__r] (auto&... __args) {
-           return _Adaptor{}(std::forward<_Range>(__r), std::move(__args)...);
-         };
-         return std::apply(__forwarder, _M_args);
-       }
-
-      template<typename _Range>
-       constexpr auto
-       operator()(_Range&& __r) const && = delete;
-#endif
-    };
-
-  // A lightweight specialization of the above primary template for
-  // the common case where _Adaptor accepts a single extra argument.
-  template<typename _Adaptor, typename _Arg>
-    struct _Partial<_Adaptor, _Arg> : _RangeAdaptorClosure<_Partial<_Adaptor, 
_Arg>>
-    {
-      _Arg _M_arg;
-
-      template<typename _Tp>
-       constexpr
-       _Partial(int, _Tp&& __arg)
-         : _M_arg(std::forward<_Tp>(__arg))
-       { }
-
-#if __cpp_explicit_this_parameter
-      template<typename _Self, typename _Range>
-       requires __adaptor_invocable<_Adaptor, _Range, __like_t<_Self, _Arg>>
-       constexpr auto
-       operator()(this _Self&& __self, _Range&& __r)
-       {
-         return _Adaptor{}(std::forward<_Range>(__r),
-                           __like_t<_Self, _Partial>(__self)._M_arg);
-       }
-#else
-      template<typename _Range>
-       requires __adaptor_invocable<_Adaptor, _Range, const _Arg&>
-       constexpr auto
-       operator()(_Range&& __r) const &
-       { return _Adaptor{}(std::forward<_Range>(__r), _M_arg); }
-
-      template<typename _Range>
-       requires __adaptor_invocable<_Adaptor, _Range, _Arg>
-       constexpr auto
-       operator()(_Range&& __r) &&
-       { return _Adaptor{}(std::forward<_Range>(__r), std::move(_M_arg)); }
+       { return _Binder::_S_call(std::move(_M_binder), 
std::forward<_Range>(__r)); }
 
       template<typename _Range>
        constexpr auto
@@ -1150,12 +1098,13 @@ namespace views::__adaptor
       && (is_trivially_copy_constructible_v<_Args> && ...)
     struct _Partial<_Adaptor, _Args...> : 
_RangeAdaptorClosure<_Partial<_Adaptor, _Args...>>
     {
-      tuple<_Args...> _M_args;
+      using _Binder = _Bind_back_t<_Adaptor, _Args...>;
+      [[no_unique_address]] _Binder _M_binder;
 
       template<typename... _Ts>
        constexpr
        _Partial(int, _Ts&&... __args)
-         : _M_args(std::forward<_Ts>(__args)...)
+         : _M_binder(0, _Adaptor(), std::forward<_Ts>(__args)...)
        { }
 
       // Invoke _Adaptor with arguments __r, const _M_args&... regardless
@@ -1164,36 +1113,7 @@ namespace views::__adaptor
        requires __adaptor_invocable<_Adaptor, _Range, const _Args&...>
        constexpr auto
        operator()(_Range&& __r) const
-       {
-         auto __forwarder = [&__r] (const auto&... __args) {
-           return _Adaptor{}(std::forward<_Range>(__r), __args...);
-         };
-         return std::apply(__forwarder, _M_args);
-       }
-
-      static constexpr bool _S_has_simple_call_op = true;
-    };
-
-  // A lightweight specialization of the above template for the common case
-  // where _Adaptor accepts a single extra argument.
-  template<typename _Adaptor, typename _Arg>
-    requires __adaptor_has_simple_extra_args<_Adaptor, _Arg>
-      && is_trivially_copy_constructible_v<_Arg>
-    struct _Partial<_Adaptor, _Arg> : _RangeAdaptorClosure<_Partial<_Adaptor, 
_Arg>>
-    {
-      _Arg _M_arg;
-
-      template<typename _Tp>
-       constexpr
-       _Partial(int, _Tp&& __arg)
-         : _M_arg(std::forward<_Tp>(__arg))
-       { }
-
-      template<typename _Range>
-       requires __adaptor_invocable<_Adaptor, _Range, const _Arg&>
-       constexpr auto
-       operator()(_Range&& __r) const
-       { return _Adaptor{}(std::forward<_Range>(__r), _M_arg); }
+       { return _Binder::_S_call(_M_binder, std::forward<_Range>(__r)); }
 
       static constexpr bool _S_has_simple_call_op = true;
     };

Reply via email to