mirror of git://gcc.gnu.org/git/gcc.git
libstdc++: Use _Bind_front_t/_Bind_back_t in bind_front<f>/bind_back<f> [PR122032]
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 still 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 types returned by bind_front<f>(args...) and bind_back<f>(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<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. Reviewed-by: Patrick Palka <ppalka@redhat.com> Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
This commit is contained in:
parent
a645e903e8
commit
71004c2414
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue