This patch changes the implementation of bind_front<f> and bind_back<f> to
return a _Bind_front_t<_Bind_fn_t<f>, ...> and _Bind_back_t<_Bind_fn_t<f>, ...>
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<f>(s), bind_front<f>(move(s)), and
bind_front<f>(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 now returns a _Bind_fn_t<f>. Since this
type is already a perfect forwarding call wrapper, it yields the same result as
_Bind_front_t<_Bind_fn_t<f>>.
A consequence of this change is that the type returned by bind_front<f>(args...)
and bind_back<f>(args...) is no longer structural according to GCC. However, the
standard does not require these resulting functors to be structural, and lambda
with captures, that were previously returned, are not structural according to
the standard.
PR libstdc++/122032
libstdc++-v3/ChangeLog:
* include/std/functional (std::bind_front<f>, std::bind_back<f>):
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.
---
not_fn<f>() is not impacted, as it does not have any state entities,
and produce stateless lambda.
Tested on x86_64-linux. OK for trunk?
libstdc++-v3/include/std/functional | 50 +++----------------
.../function_objects/bind_back/nttp.cc | 21 +++++++-
.../function_objects/bind_front/nttp.cc | 21 +++++++-
3 files changed, 45 insertions(+), 47 deletions(-)
diff --git a/libstdc++-v3/include/std/functional
b/libstdc++-v3/include/std/functional
index e4dbb9e0c45..8a6870d71c9 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<fn>(bind_args...)` is a function object
@@ -970,32 +969,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<auto __fn, typename... _BindArgs>
constexpr decltype(auto)
bind_front(_BindArgs&&... __bind_args)
- noexcept(__and_v<is_nothrow_constructible<_BindArgs>...>)
+ noexcept(__and_v<is_nothrow_constructible<_BindArgs>...>)
{
using _Fn = decltype(__fn);
- static_assert(
- (is_constructible_v<decay_t<_BindArgs>, _BindArgs> && ...) &&
- (is_move_constructible_v<decay_t<_BindArgs>> && ...));
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))]
- <typename _Self, typename... _CallArgs>
- (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<auto __fn, typename... _BindArgs>
constexpr decltype(auto)
bind_back(_BindArgs&&... __bind_args)
- noexcept(__and_v<is_nothrow_constructible<_BindArgs>...>)
+ noexcept(__and_v<is_nothrow_constructible<_BindArgs>...>)
{
using _Fn = decltype(__fn);
- static_assert(
- (is_constructible_v<decay_t<_BindArgs>, _BindArgs> && ...) &&
- (is_move_constructible_v<decay_t<_BindArgs>> && ...));
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))]
- <typename _Self, typename... _CallArgs>
- (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<f>(std::declval<int>())),
+ decltype(bind_back<f>(std::declval<int&>()))
+ >);
+ static_assert(std::is_same_v<
+ decltype(bind_back<f>(std::declval<int>())),
+ decltype(bind_back<f>(std::declval<const int&>()))
+ >);
+
+ static_assert(std::is_same_v<
+ decltype(bind_back<f>(std::declval<int>(), std::declval<float>())),
+ decltype(bind_back<f>(std::declval<int&>(), std::declval<float&>()))
+ >);
+ static_assert(std::is_same_v<
+ decltype(bind_back<f>(std::declval<int>(), std::declval<float>())),
+ decltype(bind_back<f>(std::declval<const int&>(), std::declval<const
float&>()))
+ >);
+
// Reference wrappers should be handled:
static_assert(!std::is_same_v<
decltype(bind_back<f>(std::declval<int&>())),
@@ -270,10 +289,8 @@ test04()
VERIFY(bind_back<g>(1)(2, 3) == 3*1 + 1*2 + 2*3 );
constexpr auto g2 = bind_back<f>(1, 2);
VERIFY(g2(3) == 2*1 + 3*2 + 1*3 );
- VERIFY(bind_back<g1>(2)(3) == 3*1 + 2*2 + 1*3 );
constexpr auto g3 = bind_back<f>(1, 2, 3);
VERIFY(g3() == 1 + 2*2 + 3*3);
- VERIFY(bind_back<g2>(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<f>(std::declval<int>())),
+ decltype(bind_front<f>(std::declval<int&>()))
+ >);
+ static_assert(std::is_same_v<
+ decltype(bind_front<f>(std::declval<int>())),
+ decltype(bind_front<f>(std::declval<const int&>()))
+ >);
+
+ static_assert(std::is_same_v<
+ decltype(bind_front<f>(std::declval<int>(), std::declval<float>())),
+ decltype(bind_front<f>(std::declval<int&>(), std::declval<float&>()))
+ >);
+ static_assert(std::is_same_v<
+ decltype(bind_front<f>(std::declval<int>(), std::declval<float>())),
+ decltype(bind_front<f>(std::declval<const int&>(), std::declval<const
float&>()))
+ >);
+
// Reference wrappers should be handled:
static_assert(!std::is_same_v<
decltype(bind_front<f>(std::declval<int&>())),
@@ -269,10 +288,8 @@ test04()
VERIFY( bind_front<g>(1)(2, 3) == 1 + 2*2 + 3*3 );
constexpr auto g2 = bind_front<f>(1, 2);
VERIFY( g2(3) == 1 + 2*2 + 3*3 );
- VERIFY( bind_front<g1>(2)(3) == 1 + 2*2 + 3*3 );
constexpr auto g3 = bind_front<f>(1, 2, 3);
VERIFY( g3() == 1 + 2*2 + 3*3 );
- VERIFY(bind_front<g2>(3)() == 1 + 2*2 + 3*3 );
return true;
}
--
2.51.0