[PATCH] libstdc++: Optimized bind_front/bind_back for zero bound args.
Tomasz Kamiński
tkaminsk@redhat.com
Wed Aug 20 10:03:01 GMT 2025
This patch adjusts the implementation of bind_front(f) and bind_back(f)
(with zero bound arguments), so it returns an auto(f) (a copy of the
functor), rather than a specialization of Bind_front/Bind_back. This change
is mostly unobservable, as standard does not specify return type of
above functions, but may result in elision of move/copy operations when
by-value parameters are initialized with prvalues. See the update to
testMaterialization.
This behavior is technically not allowed by C++23 [func.require] p3,
which requires that rvalue arguments are delivered as rvalue references,
forcing their materialization. I have reported a LWG issue to make this
conforming.
The _Bind_front_t/_Bind_back_t aliases were removed, as the aliased types
are now spelled only once.
libstdc++-v3/ChangeLog:
* include/std/functional (_Bind_front_t, _Bind_back_t): Remove.
(std::bind_front, std::bind_back): Return copy of __fn for
zero bounds args.
(_Bind_front, _Bind_back): Remove check for move constructor
of _Fd, that is now part of bind_xxx functors.
* testsuite/20_util/function_objects/bind_back/1.cc: Updated
testMaterialization.
* testsuite/20_util/function_objects/bind_front/1.cc: Likewise.
Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
---
This is implemented on top of 20250820081809.50754-1-tkaminsk@redhat.com.
Do we think that this is OK for trunk? As mentioned technically not
conforming, I do not think user will care about extra move.
The goal here, would be able to say that bind_front<f>(args...) is
bind_front(nttp<f>, args...).
Tested on x86_64-linux.
libstdc++-v3/include/std/functional | 37 ++++++++++---------
.../20_util/function_objects/bind_back/1.cc | 5 ++-
.../20_util/function_objects/bind_front/1.cc | 5 ++-
3 files changed, 26 insertions(+), 21 deletions(-)
diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
index b1cda87929d..c8699ac849f 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -973,7 +973,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Fd, typename... _BoundArgs>
struct _Bind_front
{
- static_assert(is_move_constructible_v<_Fd>);
static_assert((is_move_constructible_v<_BoundArgs> && ...));
// First parameter is to ensure this constructor is never used
@@ -1081,9 +1080,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
[[no_unique_address]] _BoundArgsStorage _M_bound_args;
};
- template<typename _Fn, typename... _Args>
- using _Bind_front_t = _Bind_front<decay_t<_Fn>, decay_t<_Args>...>;
-
/** Create call wrapper by partial application of arguments to function.
*
* The result of `std::bind_front(f, args...)` is a function object that
@@ -1094,13 +1090,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* @since C++20
*/
template<typename _Fn, typename... _Args>
- constexpr _Bind_front_t<_Fn, _Args...>
+ constexpr auto
bind_front(_Fn&& __fn, _Args&&... __args)
- noexcept(is_nothrow_constructible_v<_Bind_front_t<_Fn, _Args...>,
- int, _Fn, _Args...>)
+ noexcept(__and_<is_nothrow_constructible<decay_t<_Fn>, _Fn>,
+ is_nothrow_constructible<decay_t<_Args>, _Args>...>::value)
{
- return _Bind_front_t<_Fn, _Args...>(0, std::forward<_Fn>(__fn),
- std::forward<_Args>(__args)...);
+ using _Fd = decay_t<_Fn>;
+ static_assert(is_move_constructible_v<_Fd>);
+ if constexpr (sizeof...(_Args) == 0)
+ return _Fd(std::forward<_Fn>(__fn));
+ else
+ return _Bind_front<_Fd, decay_t<_Args>...>(0, std::forward<_Fn>(__fn),
+ std::forward<_Args>(__args)...);
}
#endif // __cpp_lib_bind_front
@@ -1158,9 +1159,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
[[no_unique_address]] _BoundArgsStorage _M_bound_args;
};
- template<typename _Fn, typename... _Args>
- using _Bind_back_t = _Bind_back<decay_t<_Fn>, decay_t<_Args>...>;
-
/** Create call wrapper by partial application of arguments to function.
*
* The result of `std::bind_back(f, args...)` is a function object that
@@ -1171,13 +1169,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* @since C++23
*/
template<typename _Fn, typename... _Args>
- constexpr _Bind_back_t<_Fn, _Args...>
+ constexpr auto
bind_back(_Fn&& __fn, _Args&&... __args)
- noexcept(is_nothrow_constructible_v<_Bind_back_t<_Fn, _Args...>,
- int, _Fn, _Args...>)
+ noexcept(__and_<is_nothrow_constructible<decay_t<_Fn>, _Fn>,
+ is_nothrow_constructible<decay_t<_Args>, _Args>...>::value)
{
- return _Bind_back_t<_Fn, _Args...>(0, std::forward<_Fn>(__fn),
- std::forward<_Args>(__args)...);
+ using _Fd = decay_t<_Fn>;
+ static_assert(is_move_constructible_v<_Fd>);
+ if constexpr (sizeof...(_Args) == 0)
+ return _Fd(std::forward<_Fn>(__fn));
+ else
+ return _Bind_back<_Fd, decay_t<_Args>...>(0, std::forward<_Fn>(__fn),
+ std::forward<_Args>(__args)...);
}
#endif // __cpp_lib_bind_back
diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/1.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/1.cc
index a31528fc755..ef7531f0045 100644
--- a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/1.cc
+++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/1.cc
@@ -290,10 +290,11 @@ testMaterialization()
{ return arg.counter; };
};
- // CountedArg is bound to rvalue-reference thus moved
+ // copy of F is returned
auto f0 = std::bind_back(F{});
- VERIFY( f0(CountedArg(), 10) == 1 );
+ VERIFY( f0(CountedArg(), 10) == 0 );
+ // CountedArg is bound to rvalue-reference thus moved
auto f1 = std::bind_back(F{}, 10);
VERIFY( f1(CountedArg()) == 1 );
}
diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc
index ef28de8321b..5b0ae3772ab 100644
--- a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc
+++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc
@@ -288,10 +288,11 @@ testMaterialization()
{ return arg.counter; };
};
- // CountedArg is bound to rvalue-reference thus moved
+ // copy of F is returned
auto f0 = std::bind_front(F{});
- VERIFY( f0(10, CountedArg()) == 1 );
+ VERIFY( f0(10, CountedArg()) == 0 );
+ // CountedArg is bound to rvalue-reference thus moved
auto f1 = std::bind_front(F{}, 10);
VERIFY( f1(CountedArg()) == 1 );
}
--
2.50.1
More information about the Libstdc++
mailing list