From: Tomasz Kamiński Date: Fri, 24 Oct 2025 08:24:26 +0000 (+0200) Subject: libstdc++: Use _Bind_front_t/_Bind_back_t in bind_front/bind_back [PR122032] X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=71004c2414337bd781e1221acbcca2031bb6b614;p=thirdparty%2Fgcc.git libstdc++: Use _Bind_front_t/_Bind_back_t in bind_front/bind_back [PR122032] This patch changes the implementation of bind_front and bind_back to return a _Bind_front_t<_Bind_fn_t, ...> and _Bind_back_t<_Bind_fn_t, ...> respectively, replacing the previous lambda-based implementation. The prior use of a lambda caused non-conforming behavior with respect to C++23 [func.require] p8, which requires that bind_front(s), bind_front(move(s)), and bind_front(as_const(s)) produce the same type. Additionally, using specialized structs reduces the size of the resulting functor in certain scenarios (see PR). For the zero-argument case, the function still returns a _Bind_fn_t. Since this type is already a perfect forwarding call wrapper, it yields the same result as _Bind_front_t<_Bind_fn_t>. A consequence of this change is that the types returned by bind_front(args...) and bind_back(args...) are no longer structural - they are not required to be structural by the standard. PR libstdc++/122032 libstdc++-v3/ChangeLog: * include/std/functional (std::bind_front, std::bind_back): Define in terms of _Bind_front_t/_Bind_back_t. * testsuite/20_util/function_objects/bind_back/nttp.cc: New tests. * testsuite/20_util/function_objects/bind_front/nttp.cc: New tests. Reviewed-by: Patrick Palka Signed-off-by: Tomasz Kamiński --- diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional index dd1aa204eae..1928a27d3fd 100644 --- a/libstdc++-v3/include/std/functional +++ b/libstdc++-v3/include/std/functional @@ -957,7 +957,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #if __cpp_lib_bind_front >= 202306L - /** Create call wrapper by partial application of arguments to function. * * The result of `std::bind_front(bind_args...)` is a function object @@ -970,32 +969,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template constexpr decltype(auto) bind_front(_BindArgs&&... __bind_args) - noexcept(__and_v...>) + noexcept(__and_v...>) { using _Fn = decltype(__fn); - static_assert( - (is_constructible_v, _BindArgs> && ...) && - (is_move_constructible_v> && ...)); if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) static_assert(__fn != nullptr); if constexpr (sizeof...(_BindArgs) == 0) return _Bind_fn_t<__fn>{}; - else { - return [... __bound_args(std::forward<_BindArgs>(__bind_args))] - - (this _Self&&, _CallArgs&&... __call_args) - noexcept(is_nothrow_invocable_v< - const _Fn&, __like_t<_Self, decay_t<_BindArgs>>..., _CallArgs...>) - -> decltype(auto) - requires is_invocable_v< - const _Fn&, __like_t<_Self, decay_t<_BindArgs>>..., _CallArgs...> - { - return std::invoke(__fn, - std::forward_like<_Self>(__bound_args)..., - std::forward<_CallArgs>(__call_args)...); - }; - } + else + return _Bind_front_t<_Bind_fn_t<__fn>, _BindArgs...>(0, + _Bind_fn_t<__fn>{}, std::forward<_BindArgs>(__bind_args)...); } #endif // __cpp_lib_bind_front // C++26 @@ -1035,36 +1019,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template constexpr decltype(auto) bind_back(_BindArgs&&... __bind_args) - noexcept(__and_v...>) + noexcept(__and_v...>) { using _Fn = decltype(__fn); - static_assert( - (is_constructible_v, _BindArgs> && ...) && - (is_move_constructible_v> && ...)); if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) static_assert(__fn != nullptr); if constexpr (sizeof...(_BindArgs) == 0) return _Bind_fn_t<__fn>{}; else - { - // Capture arguments in a lambda and return that. - return [... __bound_args(std::forward<_BindArgs>(__bind_args))] - - (this _Self&&, _CallArgs&&... __call_args) - noexcept(is_nothrow_invocable_v< - const _Fn&, _CallArgs..., __like_t<_Self, decay_t<_BindArgs>>...>) - -> decltype(auto) - requires is_invocable_v< - const _Fn&, _CallArgs..., __like_t<_Self, decay_t<_BindArgs>>...> - { - return std::invoke(__fn, - std::forward<_CallArgs>(__call_args)..., - std::forward_like<_Self>(__bound_args)...); - }; - } + return _Bind_back_t<_Bind_fn_t<__fn>, _BindArgs...>(0, + _Bind_fn_t<__fn>{}, std::forward<_BindArgs>(__bind_args)...); } - #endif // __cpp_lib_bind_back // C++26, nttp #endif // __cpp_lib_bind_back @@ -1168,7 +1134,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #if __cpp_lib_not_fn >= 202306L - /** Wrap a function type to create a function object that negates its result. * * The function template `std::not_fn` creates a "forwarding call wrapper", @@ -1196,7 +1161,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION !std::invoke(__fn, std::forward<_Args>(__args)...); } { return !std::invoke(__fn, std::forward<_Args>(__args)...); }; }; - #endif // __cpp_lib_not_fn >= 202306L #endif // __cpp_lib_not_fn diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp.cc index 3bea8eced43..24bf0466e2d 100644 --- a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp.cc +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp.cc @@ -24,6 +24,25 @@ test01() struct F { void operator()(int) {} }; constexpr F f{}; + // Arguments should be decayed: + static_assert(std::is_same_v< + decltype(bind_back(std::declval())), + decltype(bind_back(std::declval())) + >); + static_assert(std::is_same_v< + decltype(bind_back(std::declval())), + decltype(bind_back(std::declval())) + >); + + static_assert(std::is_same_v< + decltype(bind_back(std::declval(), std::declval())), + decltype(bind_back(std::declval(), std::declval())) + >); + static_assert(std::is_same_v< + decltype(bind_back(std::declval(), std::declval())), + decltype(bind_back(std::declval(), std::declval())) + >); + // Reference wrappers should be handled: static_assert(!std::is_same_v< decltype(bind_back(std::declval())), @@ -270,10 +289,8 @@ test04() VERIFY(bind_back(1)(2, 3) == 3*1 + 1*2 + 2*3 ); constexpr auto g2 = bind_back(1, 2); VERIFY(g2(3) == 2*1 + 3*2 + 1*3 ); - VERIFY(bind_back(2)(3) == 3*1 + 2*2 + 1*3 ); constexpr auto g3 = bind_back(1, 2, 3); VERIFY(g3() == 1 + 2*2 + 3*3); - VERIFY(bind_back(3)() == 1*2 + 2*3 + 3*1 ); return true; } diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp.cc index 9eb3c432a86..cf84353887b 100644 --- a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp.cc +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp.cc @@ -24,6 +24,25 @@ test01() struct F { void operator()(int) {} }; constexpr F f{}; + // Arguments should be decayed: + static_assert(std::is_same_v< + decltype(bind_front(std::declval())), + decltype(bind_front(std::declval())) + >); + static_assert(std::is_same_v< + decltype(bind_front(std::declval())), + decltype(bind_front(std::declval())) + >); + + static_assert(std::is_same_v< + decltype(bind_front(std::declval(), std::declval())), + decltype(bind_front(std::declval(), std::declval())) + >); + static_assert(std::is_same_v< + decltype(bind_front(std::declval(), std::declval())), + decltype(bind_front(std::declval(), std::declval())) + >); + // Reference wrappers should be handled: static_assert(!std::is_same_v< decltype(bind_front(std::declval())), @@ -269,10 +288,8 @@ test04() VERIFY( bind_front(1)(2, 3) == 1 + 2*2 + 3*3 ); constexpr auto g2 = bind_front(1, 2); VERIFY( g2(3) == 1 + 2*2 + 3*3 ); - VERIFY( bind_front(2)(3) == 1 + 2*2 + 3*3 ); constexpr auto g3 = bind_front(1, 2, 3); VERIFY( g3() == 1 + 2*2 + 3*3 ); - VERIFY(bind_front(3)() == 1 + 2*2 + 3*3 ); return true; }