This patch refactors the implementation of bind_front and bind_back to avoid using std::tuple for argument storage. Instead, bound arguments are now: * stored directly if there is only one, * within a dedicated _Bound_arg_storage otehrwise.
_Bound_arg_storage holds each argument in an _Indexed_bound_arg base object. The base class is parameterized by both type and index to allow storing multiple arguments of the same type. Invocations are handled by _S_apply_front amd _S_apply_back static functions, which simulate explicit object parameters. To facilitate this, the __like_t alias template is now unconditionally available since C++11 in bits/move.h. libstdc++-v3/ChangeLog: * include/bits/move.h (std::__like_impl, std::__like_t): Make available in c++11. * include/std/functional (std::_Indexed_bound_arg) (std::_Bound_arg_storage, std::__make_bound_args): Define. (std::_Bind_front, std::_Bind_back): Use _Bound_arg_storage. * testsuite/20_util/function_objects/bind_back/1.cc: Expand test to cover cases of 0, 1, many bound args. * testsuite/20_util/function_objects/bind_back/111327.cc: Likewise. * testsuite/20_util/function_objects/bind_front/1.cc: Likewise. * testsuite/20_util/function_objects/bind_front/111327.cc: Likewise. Signed-off-by: Tomasz Kamiński <tkami...@redhat.com> --- Changes in v2: * replace few 8 spaces with tabs * add more test cases, covering materialization Testing on x86_64-linux, all bind test passed. OK for trunk when test passes. libstdc++-v3/include/bits/move.h | 2 +- libstdc++-v3/include/std/functional | 113 +++++++++--- .../20_util/function_objects/bind_back/1.cc | 166 ++++++++++++++++-- .../function_objects/bind_back/111327.cc | 11 ++ .../20_util/function_objects/bind_front/1.cc | 164 +++++++++++++++-- .../function_objects/bind_front/111327.cc | 11 ++ 6 files changed, 418 insertions(+), 49 deletions(-) diff --git a/libstdc++-v3/include/bits/move.h b/libstdc++-v3/include/bits/move.h index 061e6b4de3d..8c4f461a110 100644 --- a/libstdc++-v3/include/bits/move.h +++ b/libstdc++-v3/include/bits/move.h @@ -89,7 +89,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return static_cast<_Tp&&>(__t); } -#if __glibcxx_forward_like // C++ >= 23 template<typename _Tp, typename _Up> struct __like_impl; // _Tp must be a reference and _Up an lvalue reference @@ -112,6 +111,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp, typename _Up> using __like_t = typename __like_impl<_Tp&&, _Up&>::type; +#if __glibcxx_forward_like // C++ >= 23 /** @brief Forward with the cv-qualifiers and value category of another type. * @tparam _Tp An lvalue reference or rvalue reference. * @tparam _Up An lvalue reference type deduced from the function argument. diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional index 307bcb95bcc..b1cda87929d 100644 --- a/libstdc++-v3/include/std/functional +++ b/libstdc++-v3/include/std/functional @@ -922,6 +922,53 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #ifdef __cpp_lib_bind_front // C++ >= 20 + template<size_t, typename _Tp> + struct _Indexed_bound_arg + { + [[no_unique_address]] _Tp _M_val; + }; + + template<typename... _IndexedArgs> + struct _Bound_arg_storage : _IndexedArgs... + { + template<typename _Fd, typename _Self, typename... _CallArgs> + static constexpr + decltype(auto) + _S_apply_front(_Fd&& __fd, _Self&& __self, _CallArgs&&... __call_args) + { + return std::invoke(std::forward<_Fd>(__fd), + __like_t<_Self, _IndexedArgs>(__self)._M_val..., + std::forward<_CallArgs>(__call_args)...); + } + + template<typename _Fd, typename _Self, typename... _CallArgs> + static constexpr + decltype(auto) + _S_apply_back(_Fd&& __fd, _Self&& __self, _CallArgs&&... __call_args) + { + return std::invoke(std::forward<_Fd>(__fd), + std::forward<_CallArgs>(__call_args)..., + __like_t<_Self, _IndexedArgs>(__self)._M_val...); + } + }; + + template<typename... _BoundArgs, typename... _Args> + constexpr auto + __make_bound_args(_Args&&... __args) + { + if constexpr (sizeof...(_BoundArgs) == 1) + // pack has one element, so return copy of arg + return (_BoundArgs(std::forward<_Args>(__args)), ...); + else + { + auto __impl = [&]<size_t... _Inds>(index_sequence<_Inds...>) + { + return _Bound_arg_storage<_Indexed_bound_arg<_Inds, _BoundArgs>...> + { {_BoundArgs(std::forward<_Args>(__args))}... }; + }; + return __impl(index_sequence_for<_BoundArgs...>()); + } + } template<typename _Fd, typename... _BoundArgs> struct _Bind_front @@ -937,7 +984,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(__and_<is_nothrow_constructible<_Fd, _Fn>, is_nothrow_constructible<_BoundArgs, _Args>...>::value) : _M_fd(std::forward<_Fn>(__fn)), - _M_bound_args(std::forward<_Args>(__args)...) + _M_bound_args(__make_bound_args<_BoundArgs...>(std::forward<_Args>(__args)...)) { static_assert(sizeof...(_Args) == sizeof...(_BoundArgs)); } #if __cpp_explicit_this_parameter @@ -948,7 +995,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(is_nothrow_invocable_v<__like_t<_Self, _Fd>, __like_t<_Self, _BoundArgs>..., _CallArgs...>) { - return _S_call(__like_t<_Self, _Bind_front>(__self), _BoundIndices(), + return _S_call(__like_t<_Self, _Bind_front>(__self), std::forward<_CallArgs>(__call_args)...); } #else @@ -959,8 +1006,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION operator()(_CallArgs&&... __call_args) & noexcept(is_nothrow_invocable_v<_Fd&, _BoundArgs&..., _CallArgs...>) { - return _S_call(*this, _BoundIndices(), - std::forward<_CallArgs>(__call_args)...); + return _S_call(*this, std::forward<_CallArgs>(__call_args)...); } template<typename... _CallArgs> @@ -971,8 +1017,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(is_nothrow_invocable_v<const _Fd&, const _BoundArgs&..., _CallArgs...>) { - return _S_call(*this, _BoundIndices(), - std::forward<_CallArgs>(__call_args)...); + return _S_call(*this, std::forward<_CallArgs>(__call_args)...); } template<typename... _CallArgs> @@ -982,8 +1027,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION operator()(_CallArgs&&... __call_args) && noexcept(is_nothrow_invocable_v<_Fd, _BoundArgs..., _CallArgs...>) { - return _S_call(std::move(*this), _BoundIndices(), - std::forward<_CallArgs>(__call_args)...); + return _S_call(std::move(*this), + std::forward<_CallArgs>(__call_args)...); } template<typename... _CallArgs> @@ -994,8 +1039,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(is_nothrow_invocable_v<const _Fd, const _BoundArgs..., _CallArgs...>) { - return _S_call(std::move(*this), _BoundIndices(), - std::forward<_CallArgs>(__call_args)...); + return _S_call(std::move(*this), + std::forward<_CallArgs>(__call_args)...); } template<typename... _CallArgs> @@ -1012,20 +1057,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif private: - using _BoundIndices = index_sequence_for<_BoundArgs...>; + using _BoundArgsStorage + // _BoundArgs are required to be move-constructible, so this is valid. + = decltype(__make_bound_args<_BoundArgs...>(std::declval<_BoundArgs>()...)); - template<typename _Tp, size_t... _Ind, typename... _CallArgs> + template<typename _Tp, typename... _CallArgs> static constexpr decltype(auto) - _S_call(_Tp&& __g, index_sequence<_Ind...>, _CallArgs&&... __call_args) + _S_call(_Tp&& __g, _CallArgs&&... __call_args) { - return std::invoke(std::forward<_Tp>(__g)._M_fd, - std::get<_Ind>(std::forward<_Tp>(__g)._M_bound_args)..., - std::forward<_CallArgs>(__call_args)...); + if constexpr (sizeof...(_BoundArgs) == 1) + return std::invoke(std::forward<_Tp>(__g)._M_fd, + std::forward<_Tp>(__g)._M_bound_args, + std::forward<_CallArgs>(__call_args)...); + else + return _BoundArgsStorage::_S_apply_front( + std::forward<_Tp>(__g)._M_fd, + std::forward<_Tp>(__g)._M_bound_args, + std::forward<_CallArgs>(__call_args)...); } [[no_unique_address]] _Fd _M_fd; - [[no_unique_address]] std::tuple<_BoundArgs...> _M_bound_args; + [[no_unique_address]] _BoundArgsStorage _M_bound_args; }; template<typename _Fn, typename... _Args> @@ -1066,7 +1119,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(__and_<is_nothrow_constructible<_Fd, _Fn>, is_nothrow_constructible<_BoundArgs, _Args>...>::value) : _M_fd(std::forward<_Fn>(__fn)), - _M_bound_args(std::forward<_Args>(__args)...) + _M_bound_args(__make_bound_args<_BoundArgs...>(std::forward<_Args>(__args)...)) { static_assert(sizeof...(_Args) == sizeof...(_BoundArgs)); } template<typename _Self, typename... _CallArgs> @@ -1076,25 +1129,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(is_nothrow_invocable_v<__like_t<_Self, _Fd>, _CallArgs..., __like_t<_Self, _BoundArgs>...>) { - return _S_call(__like_t<_Self, _Bind_back>(__self), _BoundIndices(), + return _S_call(__like_t<_Self, _Bind_back>(__self), std::forward<_CallArgs>(__call_args)...); } private: - using _BoundIndices = index_sequence_for<_BoundArgs...>; + using _BoundArgsStorage + // _BoundArgs are required to be move-constructible, so this is valid. + = decltype(__make_bound_args<_BoundArgs...>(std::declval<_BoundArgs>()...)); - template<typename _Tp, size_t... _Ind, typename... _CallArgs> + template<typename _Tp, typename... _CallArgs> static constexpr decltype(auto) - _S_call(_Tp&& __g, index_sequence<_Ind...>, _CallArgs&&... __call_args) + _S_call(_Tp&& __g, _CallArgs&&... __call_args) { - return std::invoke(std::forward<_Tp>(__g)._M_fd, - std::forward<_CallArgs>(__call_args)..., - std::get<_Ind>(std::forward<_Tp>(__g)._M_bound_args)...); + if constexpr (sizeof...(_BoundArgs) == 1) + return std::invoke(std::forward<_Tp>(__g)._M_fd, + std::forward<_CallArgs>(__call_args)..., + std::forward<_Tp>(__g)._M_bound_args); + else + return _BoundArgsStorage::_S_apply_back( + std::forward<_Tp>(__g)._M_fd, + std::forward<_Tp>(__g)._M_bound_args, + std::forward<_CallArgs>(__call_args)...); } [[no_unique_address]] _Fd _M_fd; - [[no_unique_address]] std::tuple<_BoundArgs...> _M_bound_args; + [[no_unique_address]] _BoundArgsStorage _M_bound_args; }; template<typename _Fn, typename... _Args> 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 c31d3228815..a31528fc755 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 @@ -48,6 +48,16 @@ test01() decltype(bind_back(std::declval<const F&>(), std::declval<const int&>())) >); + static_assert(std::is_same_v< + decltype(bind_back(std::declval<F>(), std::declval<int>(), std::declval<float>())), + decltype(bind_back(std::declval<F&>(), std::declval<int&>(), std::declval<float&>())) + >); + static_assert(std::is_same_v< + decltype(bind_back(std::declval<F>(), std::declval<int>(), std::declval<float>())), + decltype(bind_back(std::declval<const F&>(), std::declval<const int&>(), std::declval<const float&>())) + >); + + // Reference wrappers should be handled: static_assert(!std::is_same_v< decltype(bind_back(std::declval<F>(), std::declval<int&>())), @@ -63,29 +73,58 @@ test01() >); } +struct quals +{ + bool as_const; + bool as_lvalue; +}; + +template<typename... Args> void -test02() +testTarget(Args... args) { - struct quals + struct F { - bool as_const; - bool as_lvalue; + quals operator()(Args...) & { return { false, true }; } + quals operator()(Args...) const & { return { true, true }; } + quals operator()(Args...) && { return { false, false }; } + quals operator()(Args...) const && { return { true, false }; } }; + F f; + auto g = bind_back(f, args...); + const auto& cg = g; + quals q; + + // constness and value category should be forwarded to the target object: + q = g(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(g)(); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = cg(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(cg)(); + VERIFY( q.as_const && ! q.as_lvalue ); +} + +template<typename... Args> +void +testBoundArgs(Args... args) +{ struct F { - quals operator()() & { return { false, true }; } - quals operator()() const & { return { true, true }; } - quals operator()() && { return { false, false }; } - quals operator()() const && { return { true, false }; } + quals operator()(Args..., int&) const { return { false, true }; } + quals operator()(Args..., int const&) const { return { true, true }; } + quals operator()(Args..., int&&) const { return { false, false }; } + quals operator()(Args..., int const&&) const { return { true, false }; } }; F f; - auto g = bind_back(f); + auto g = bind_back(f, args..., 10); const auto& cg = g; quals q; - // constness and value category should be forwarded to the target object: + // constness and value category should be forwarded to the bound objects: q = g(); VERIFY( ! q.as_const && q.as_lvalue ); q = std::move(g)(); @@ -94,6 +133,70 @@ test02() VERIFY( q.as_const && q.as_lvalue ); q = std::move(cg)(); VERIFY( q.as_const && ! q.as_lvalue ); + + int i = 0; + auto gr = bind_back(f, args..., std::ref(i)); + const auto& cgr = gr; + + // bound object is reference wrapper + q = gr(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(gr)(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = cgr(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(cgr)(); + VERIFY( ! q.as_const && q.as_lvalue ); + + auto gcr = bind_back(f, args..., std::cref(i)); + const auto& cgcr = gcr; + + q = gcr(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(gcr)(); + VERIFY( q.as_const && q.as_lvalue ); + q = cgcr(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(cgcr)(); + VERIFY( q.as_const && q.as_lvalue ); +} + +template<typename... Args> +void +testCallArgs(Args... args) +{ + struct F + { + quals operator()(int&, Args...) const { return { false, true }; } + quals operator()(int const&, Args...) const { return { true, true }; } + quals operator()(int&&, Args...) const { return { false, false }; } + quals operator()(int const&&, Args...) const { return { true, false }; } + }; + + F f; + auto g = bind_back(f, args...); + const auto& cg = g; + quals q; + int i = 10; + const int ci = i; + + q = g(i); + VERIFY( ! q.as_const && q.as_lvalue ); + q = g(std::move(i)); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = g(ci); + VERIFY( q.as_const && q.as_lvalue ); + q = g(std::move(ci)); + VERIFY( q.as_const && ! q.as_lvalue ); + + q = cg(i); + VERIFY( ! q.as_const && q.as_lvalue ); + q = cg(std::move(i)); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = cg(ci); + VERIFY( q.as_const && q.as_lvalue ); + q = cg(std::move(ci)); + VERIFY( q.as_const && ! q.as_lvalue ); } void @@ -168,11 +271,52 @@ test04() return true; } +struct CountedArg +{ + CountedArg() = default; + CountedArg(CountedArg&& f) noexcept : counter(f.counter) { ++counter; } + CountedArg& operator=(CountedArg&&) = delete; + + int counter = 0; +}; +CountedArg const c; + +void +testMaterialization() +{ + struct F + { + int operator()(CountedArg arg, int) const + { return arg.counter; }; + }; + + // CountedArg is bound to rvalue-reference thus moved + auto f0 = std::bind_back(F{}); + VERIFY( f0(CountedArg(), 10) == 1 ); + + auto f1 = std::bind_back(F{}, 10); + VERIFY( f1(CountedArg()) == 1 ); +} + int main() { test01(); - test02(); test03(); + + testTarget(); + testTarget(10); + testTarget(10, 20, 30); + + testBoundArgs(); + testBoundArgs(10); + testBoundArgs(10, 20, 30); + + testCallArgs(); + testCallArgs(10); + testCallArgs(10, 20, 30); + + testMaterialization(); + static_assert(test04()); } diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/111327.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/111327.cc index d634db9dc1d..de3ae47e37f 100644 --- a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/111327.cc +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/111327.cc @@ -37,6 +37,17 @@ int main() { g1(); // { dg-error "deleted|no match" } std::move(g1)(); // { dg-error "deleted|no match" } std::move(std::as_const(g1))(); + + auto f2 = std::bind_back(F{}, 42, 10); + f2(); // { dg-error "deleted|no match" } + std::move(f2)(); + std::as_const(f2)(); + std::move(std::as_const(f2))(); + + auto g2 = std::bind_back(G{}, 42, 10); + g2(); // { dg-error "deleted|no match" } + std::move(g2)(); // { dg-error "deleted|no match" } + std::move(std::as_const(g2))(); } // { dg-error "no type named 'type' in 'struct std::invoke_result" "" { target c++23 } 0 } 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 57482c52263..ef28de8321b 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 @@ -48,6 +48,15 @@ test01() decltype(bind_front(std::declval<const F&>(), std::declval<const int&>())) >); + static_assert(std::is_same_v< + decltype(bind_front(std::declval<F>(), std::declval<int>(), std::declval<float>())), + decltype(bind_front(std::declval<F&>(), std::declval<int&>(), std::declval<float&>())) + >); + static_assert(std::is_same_v< + decltype(bind_front(std::declval<F>(), std::declval<int>(), std::declval<float>())), + decltype(bind_front(std::declval<const F&>(), std::declval<const int&>(), std::declval<const float&>())) + >); + // Reference wrappers should be handled: static_assert(!std::is_same_v< decltype(bind_front(std::declval<F>(), std::declval<int&>())), @@ -63,29 +72,58 @@ test01() >); } +struct quals +{ + bool as_const; + bool as_lvalue; +}; + +template<typename... Args> void -test02() +testTarget(Args... args) { - struct quals + struct F { - bool as_const; - bool as_lvalue; + quals operator()(Args...) & { return { false, true }; } + quals operator()(Args...) const & { return { true, true }; } + quals operator()(Args...) && { return { false, false }; } + quals operator()(Args...) const && { return { true, false }; } }; + F f; + auto g = bind_front(f, args...); + const auto& cg = g; + quals q; + + // constness and value category should be forwarded to the target object: + q = g(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(g)(); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = cg(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(cg)(); + VERIFY( q.as_const && ! q.as_lvalue ); +} + +template<typename... Args> +void +testBoundArgs(Args... args) +{ struct F { - quals operator()() & { return { false, true }; } - quals operator()() const & { return { true, true }; } - quals operator()() && { return { false, false }; } - quals operator()() const && { return { true, false }; } + quals operator()(Args..., int&) const { return { false, true }; } + quals operator()(Args..., int const&) const { return { true, true }; } + quals operator()(Args..., int&&) const { return { false, false }; } + quals operator()(Args..., int const&&) const { return { true, false }; } }; F f; - auto g = bind_front(f); + auto g = bind_front(f, args..., 10); const auto& cg = g; quals q; - // constness and value category should be forwarded to the target object: + // constness and value category should be forwarded to the bound objects: q = g(); VERIFY( ! q.as_const && q.as_lvalue ); q = std::move(g)(); @@ -94,6 +132,70 @@ test02() VERIFY( q.as_const && q.as_lvalue ); q = std::move(cg)(); VERIFY( q.as_const && ! q.as_lvalue ); + + int i = 0; + auto gr = bind_front(f, args..., std::ref(i)); + const auto& cgr = gr; + + // bound object is reference wrapper, converts to same type of reference + q = gr(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(gr)(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = cgr(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(cgr)(); + VERIFY( ! q.as_const && q.as_lvalue ); + + auto gcr = bind_front(f, args..., std::cref(i)); + const auto& cgcr = gcr; + + q = gcr(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(gcr)(); + VERIFY( q.as_const && q.as_lvalue ); + q = cgcr(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(cgcr)(); + VERIFY( q.as_const && q.as_lvalue ); +} + +template<typename... Args> +void +testCallArgs(Args... args) +{ + struct F + { + quals operator()(Args..., int&) const { return { false, true }; } + quals operator()(Args..., int const&) const { return { true, true }; } + quals operator()(Args..., int&&) const { return { false, false }; } + quals operator()(Args..., int const&&) const { return { true, false }; } + }; + + F f; + auto g = bind_front(f, args...); + const auto& cg = g; + quals q; + int i = 10; + const int ci = i; + + q = g(i); + VERIFY( ! q.as_const && q.as_lvalue ); + q = g(std::move(i)); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = g(ci); + VERIFY( q.as_const && q.as_lvalue ); + q = g(std::move(ci)); + VERIFY( q.as_const && ! q.as_lvalue ); + + q = cg(i); + VERIFY( ! q.as_const && q.as_lvalue ); + q = cg(std::move(i)); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = cg(ci); + VERIFY( q.as_const && q.as_lvalue ); + q = cg(std::move(ci)); + VERIFY( q.as_const && ! q.as_lvalue ); } void @@ -167,11 +269,51 @@ test04() VERIFY( bind_front(g2, 3)() == 6 ); } +struct CountedArg +{ + CountedArg() = default; + CountedArg(CountedArg&& f) noexcept : counter(f.counter) { ++counter; } + CountedArg& operator=(CountedArg&&) = delete; + + int counter = 0; +}; +CountedArg const c; + +void +testMaterialization() +{ + struct F + { + int operator()(int, CountedArg arg) const + { return arg.counter; }; + }; + + // CountedArg is bound to rvalue-reference thus moved + auto f0 = std::bind_front(F{}); + VERIFY( f0(10, CountedArg()) == 1 ); + + auto f1 = std::bind_front(F{}, 10); + VERIFY( f1(CountedArg()) == 1 ); +} + int main() { test01(); - test02(); test03(); test04(); + + testTarget(); + testTarget(10); + testTarget(10, 20, 30); + + testBoundArgs(); + testBoundArgs(10); + testBoundArgs(10, 20, 30); + + testCallArgs(); + testCallArgs(10); + testCallArgs(10, 20, 30); + + testMaterialization(); } diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc index 5fe0a83baec..6694322d67e 100644 --- a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc @@ -37,6 +37,17 @@ int main() { g1(); // { dg-error "deleted|no match" } std::move(g1)(); // { dg-error "deleted|no match" } std::move(std::as_const(g1))(); + + auto f2 = std::bind_front(F{}, 42, 10); + f2(); // { dg-error "deleted|no match" } + std::move(f2)(); + std::as_const(f2)(); + std::move(std::as_const(f2))(); + + auto g2 = std::bind_front(G{}, 42, 10); + g2(); // { dg-error "deleted|no match" } + std::move(g2)(); // { dg-error "deleted|no match" } + std::move(std::as_const(g2))(); } // { dg-error "no type named 'type' in 'struct std::invoke_result" "" { target c++23 } 0 } -- 2.50.1