On Tue, Jul 22, 2025 at 11:28 AM Tomasz Kaminski <tkami...@redhat.com> wrote:
> > > On Tue, Jul 22, 2025 at 9:22 AM Nathan Myers <n...@cantrip.org> wrote: > >> This should be close to ready. However, std::is_invocable >> and noexcept still fail oddly applied to the not_fp result. >> The remaining failing test cases in */nttp.cc, commented >> out, need careful examination to see whether they should >> be expecting different results given that the argument >> function object is necessarily const. >> >> Changes in v4: >> * For the no-bound-arguments case, bind_front and bind_back both >> return a zero-size _BindFn_t with static operator(). >> * For the normal case, the lambda functions returned are declared >> to yield std::invoke_result_t<> instead of decltype(auto), which >> produces different test outcomes. >> >> Changes in v3: >> * NTTP functions bind_front, bind_back are self-contained. >> * No tuples: bind_front and bind_back return a simple lambda. >> * bind_front, _back with no arguments simply return the template >> parameter function. >> * Forwarded-argument passing is disciplined. >> * NTTP not_fn uses a helper struct with static op(). >> * Many more of tests that pass non-NTTP versions also pass >> >> Add non-type template parameter function-object/-pointer argument >> versions of bind_front, bind_back, and not_fn. >> >> libstdc++-v3/ChangeLog: >> PR libstdc++/119744 >> * include/bits/version.def: Redefine __cpp_lib_bind_front etc. >> * include/bits/version.h: Ditto. >> * include/std/functional: Add new bind_front etc. overloads >> * testsuite/20_util/function_objects/bind_back/1.cc >> * testsuite/20_util/function_objects/bind_back/nttp.cc >> * testsuite/20_util/function_objects/bind_front/1.cc >> * testsuite/20_util/function_objects/bind_front/nttp.cc >> * testsuite/20_util/function_objects/not_fn/nttp.cc >> * testsuite/20_util/headers/functional/synopsis.cc >> --- >> libstdc++-v3/include/bits/version.def | 12 + >> libstdc++-v3/include/bits/version.h | 21 +- >> libstdc++-v3/include/std/functional | 159 ++++++++++++- >> .../20_util/function_objects/bind_back/1.cc | 22 +- >> .../function_objects/bind_back/nttp.cc | 224 ++++++++++++++++++ >> .../20_util/function_objects/bind_front/1.cc | 16 +- >> .../function_objects/bind_front/nttp.cc | 218 +++++++++++++++++ >> .../20_util/function_objects/not_fn/nttp.cc | 113 +++++++++ >> .../20_util/headers/functional/synopsis.cc | 21 ++ >> 9 files changed, 781 insertions(+), 25 deletions(-) >> create mode 100644 >> libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp.cc >> create mode 100644 >> libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp.cc >> create mode 100644 >> libstdc++-v3/testsuite/20_util/function_objects/not_fn/nttp.cc >> >> diff --git a/libstdc++-v3/include/bits/version.def >> b/libstdc++-v3/include/bits/version.def >> index 2f70a529927..7909a7b194a 100644 >> --- a/libstdc++-v3/include/bits/version.def >> +++ b/libstdc++-v3/include/bits/version.def >> @@ -463,6 +463,10 @@ ftms = { >> >> ftms = { >> name = not_fn; >> + values = { >> + v = 202306; >> + cxxmin = 26; >> + }; >> values = { >> v = 201603; >> cxxmin = 17; >> @@ -776,6 +780,10 @@ ftms = { >> >> ftms = { >> name = bind_front; >> + values = { >> + v = 202306; >> + cxxmin = 26; >> + }; >> values = { >> v = 201907; >> cxxmin = 20; >> @@ -784,6 +792,10 @@ ftms = { >> >> ftms = { >> name = bind_back; >> + values = { >> + v = 202306; >> + cxxmin = 26; >> + }; >> values = { >> v = 202202; >> cxxmin = 23; >> diff --git a/libstdc++-v3/include/bits/version.h >> b/libstdc++-v3/include/bits/version.h >> index 8e0ae682251..9721d1d23fe 100644 >> --- a/libstdc++-v3/include/bits/version.h >> +++ b/libstdc++-v3/include/bits/version.h >> @@ -511,7 +511,12 @@ >> #undef __glibcxx_want_make_from_tuple >> >> #if !defined(__cpp_lib_not_fn) >> -# if (__cplusplus >= 201703L) >> +# if (__cplusplus > 202302L) >> +# define __glibcxx_not_fn 202306L >> +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_not_fn) >> +# define __cpp_lib_not_fn 202306L >> +# endif >> +# elif (__cplusplus >= 201703L) >> # define __glibcxx_not_fn 201603L >> # if defined(__glibcxx_want_all) || defined(__glibcxx_want_not_fn) >> # define __cpp_lib_not_fn 201603L >> @@ -866,7 +871,12 @@ >> #undef __glibcxx_want_atomic_value_initialization >> >> #if !defined(__cpp_lib_bind_front) >> -# if (__cplusplus >= 202002L) >> +# if (__cplusplus > 202302L) >> +# define __glibcxx_bind_front 202306L >> +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_bind_front) >> +# define __cpp_lib_bind_front 202306L >> +# endif >> +# elif (__cplusplus >= 202002L) >> # define __glibcxx_bind_front 201907L >> # if defined(__glibcxx_want_all) || defined(__glibcxx_want_bind_front) >> # define __cpp_lib_bind_front 201907L >> @@ -876,7 +886,12 @@ >> #undef __glibcxx_want_bind_front >> >> #if !defined(__cpp_lib_bind_back) >> -# if (__cplusplus >= 202100L) && (__cpp_explicit_this_parameter) >> +# if (__cplusplus > 202302L) >> +# define __glibcxx_bind_back 202306L >> +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_bind_back) >> +# define __cpp_lib_bind_back 202306L >> +# endif >> +# elif (__cplusplus >= 202100L) && (__cpp_explicit_this_parameter) >> # define __glibcxx_bind_back 202202L >> # if defined(__glibcxx_want_all) || defined(__glibcxx_want_bind_back) >> # define __cpp_lib_bind_back 202202L >> diff --git a/libstdc++-v3/include/std/functional >> b/libstdc++-v3/include/std/functional >> index 307bcb95bcc..03375e2a32a 100644 >> --- a/libstdc++-v3/include/std/functional >> +++ b/libstdc++-v3/include/std/functional >> @@ -71,6 +71,7 @@ >> #include <type_traits> >> #include <bits/functional_hash.h> >> #include <bits/invoke.h> >> +#include <bits/move.h> >> #include <bits/refwrap.h> // std::reference_wrapper and >> _Mem_fn_traits >> #if _GLIBCXX_HOSTED >> # include <bits/std_function.h> // std::function >> @@ -921,6 +922,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> >> std::forward<_BoundArgs>(__args)...); >> } >> >> +#if __cpp_lib_bind_front >= 202306 || __cpp_lib_bind_front >= 202306 >> > This should say __cpp_lib_bind_front || __cpp_lib_bind_back > >> + template <auto __fn> >> + struct _BindFn_t >> + { >> + using _Fn = decltype(__fn); >> + >> + template <typename... _Args> >> + constexpr static auto >> + operator()(_Args... __args) >> + noexcept(is_nothrow_invocable_v<_Fn, _Args...>) >> + -> invoke_result_t< _Fn, _Args...> >> > Once you check is_invocable_v this could be simplyu decltype(auto). > >> + requires (is_invocable_v< _Fn, _Args...>) >> + { return invoke(__fn, forward<_Args>(__args)...); } >> + >> + void operator=(this auto&&, _BindFn_t&&) = delete; >> + }; >> +#endif >> + >> #ifdef __cpp_lib_bind_front // C++ >= 20 >> >> template<typename _Fd, typename... _BoundArgs> >> @@ -940,7 +959,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> _M_bound_args(std::forward<_Args>(__args)...) >> { static_assert(sizeof...(_Args) == sizeof...(_BoundArgs)); } >> >> -#if __cpp_explicit_this_parameter >> +#ifdef __cpp_explicit_this_parameter >> template<typename _Self, typename... _CallArgs> >> constexpr >> invoke_result_t<__like_t<_Self, _Fd>, __like_t<_Self, >> _BoundArgs>..., _CallArgs...> >> @@ -1049,6 +1068,52 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> return _Bind_front_t<_Fn, _Args...>(0, std::forward<_Fn>(__fn), >> std::forward<_Args>(__args)...); >> } >> + >> +#if __cpp_lib_bind_front >= 202306 >> + >> + /** Create call wrapper by partial application of arguments to >> function. >> + * >> + * The result of `std::bind_front<fn>(bind_args...)` is a function >> object >> + * that stores the bound arguments, `bind_args...`. When that function >> + * object is invoked with `call_args...` it returns the result of >> calling >> + * `fn(bind_args..., call_args...)`. >> + * >> + * @since C++26 >> + */ >> + template<auto __fn, typename... _BindArgs> >> + constexpr decltype(auto) >> + bind_front(_BindArgs&&... __bind_args) >> + noexcept(__and_<is_nothrow_constructible<_BindArgs>...>::value) >> + requires ( >> + (is_constructible_v<decay_t<_BindArgs>, _BindArgs> and ...) and >> + (is_move_constructible_v<decay_t<_BindArgs>> and ...)) >> + { >> + using _Fn = decltype(__fn); >> + if constexpr (is_pointer_v<_Fn> or is_member_pointer_v<_Fn>) { >> > > >> + static_assert(__fn != nullptr); >> + } >> + if constexpr (sizeof...(_BindArgs) == 0) { >> + return _BindFn_t<__fn>{}; >> + } else { >> > The formatting style is a bit different in libstdc++ and braces are in new > line and intendent, something > like: > if constexpr (sizeof...(_BindArgs) == 0) > // no braces, 2 space indent from if, same as brace would be > return _BindFn_t<__fn>{}; > // else at same indentation as if > else > // two space indent > { > // additional two space vs {, and 4 from else in total > // Capture arguments in a lambda and return that. > + // 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< > + _Fn, __like_t<_Self, decay_t<_BindArgs>>..., _CallArgs...>) > In any of this check, _Fn should be replaced with const _Fn& type, because we are always referring to template parameter, which is const lvalue. > + -> invoke_result_t< > + _Fn, __like_t<_Self, decay_t<_BindArgs>>..., _CallArgs...> > Once we checked is_invocable_v this could be simply decltype(auto), > reducing the number of instantiation. You can use -> decltype(auto) in > lambda > >> + requires (is_invocable_v< >> + _Fn, __like_t<_Self, decay_t<_BindArgs>>..., _CallArgs...>) >> > No parenthesis here. > >> + { >> + return invoke(__fn, >> + forward_like<_Self>(__bound_args)..., >> + forward<_CallArgs>(__call_args)...); >> + }; >> + } >> + } >> + >> +#endif // __cpp_lib_bind_front // C++26 >> #endif // __cpp_lib_bind_front >> >> #ifdef __cpp_lib_bind_back // C++ >= 23 >> @@ -1118,6 +1183,52 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> return _Bind_back_t<_Fn, _Args...>(0, std::forward<_Fn>(__fn), >> std::forward<_Args>(__args)...); >> } >> + >> +#if __cpp_lib_bind_back >= 202306 >> + >> + /** Create call wrapper by partial application of arguments to >> function. >> + * >> + * The result of `std::bind_back<fn>(bind_args...)` is a function >> object >> + * that stores the arguments, `bind_args...`. When that function object >> + * is invoked with `call_args...` it returns the result of calling >> + * `fn(call_args..., bind_args...)`. >> + * >> + * @since C++26 >> + */ >> + template<auto __fn, typename... _BindArgs> >> + constexpr decltype(auto) >> + bind_back(_BindArgs&&... __bind_args) >> + noexcept(__and_<is_nothrow_constructible<_BindArgs>...>::value) >> + requires ( >> + (is_constructible_v<decay_t<_BindArgs>, _BindArgs> and ...) and >> + (is_move_constructible_v<decay_t<_BindArgs>> and ...)) >> + { >> > Same comments as above. > >> + using _Fn = decltype(__fn); >> + if constexpr (is_pointer_v<_Fn> or is_member_pointer_v<_Fn>) { >> + static_assert(__fn != nullptr); >> + } >> + if constexpr (sizeof...(_BindArgs) == 0) { >> + return _BindFn_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< >> + _Fn, _CallArgs..., __like_t<_Self, decay_t<_BindArgs>>...>) >> + -> invoke_result_t< >> + _Fn, _CallArgs..., __like_t<_Self, decay_t<_BindArgs>>...> >> + requires (is_invocable_v< >> + _Fn, _CallArgs..., __like_t<_Self, decay_t<_BindArgs>>...>) >> > + { >> + return invoke(__fn, >> + forward<_CallArgs>(__call_args)..., >> + forward_like<_Self>(__bound_args)...); >> + }; >> + } >> + } >> + >> +#endif // __cpp_lib_bind_back // C++26, nttp >> #endif // __cpp_lib_bind_back >> >> #if __cplusplus >= 201402L >> @@ -1212,13 +1323,55 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> */ >> template<typename _Fn> >> _GLIBCXX20_CONSTEXPR >> - inline auto >> + inline decltype(auto) >> > `auto` here is fine, as we are returning a prvalue of function wrapper > here. > >> not_fn(_Fn&& __fn) >> noexcept(std::is_nothrow_constructible<std::decay_t<_Fn>, >> _Fn&&>::value) >> { >> return _Not_fn<std::decay_t<_Fn>>{std::forward<_Fn>(__fn), 0}; >> } >> -#endif >> + >> +#if __cpp_lib_not_fn >= 202306 >> + >> + template<auto __fn> >> + struct _Not_fn_nttp >> + { >> + template<typename... _Args> >> + constexpr static decltype(auto) >> + operator()(_Args&&... __args) >> + noexcept( noexcept( >> + not std::invoke(__fn, std::forward<_Args>(__args)...) )) >> > There is a gcc/clang -fno-operator-names configuration option to disable > not as alternative keyword. > You should use ! here, > >> + requires requires { >> + not std::invoke(__fn, std::forward<_Args>(__args)...); } >> + { >> + return not std::invoke(__fn, std::forward<_Args>(__args)...); >> + } >> + }; >> + >> + /** Wrap a function type to create a function object that negates its >> result. >> + * >> + * The function template `std::not_fn` creates a "forwarding call >> wrapper", >> + * which is a function object that when called forwards its arguments >> to >> + * its invocable template argument. >> + * >> + * The result of invoking the wrapper is the negation (using `!`) of >> + * the wrapped function object. >> + * >> + * @ingroup functors >> + * @since C++26 >> + */ >> + template<auto __fn> >> + constexpr decltype(auto) >> + not_fn() noexcept >> + { >> + using _Fn = decltype(__fn); >> + if constexpr (is_pointer_v<_Fn> or is_member_pointer_v<_Fn>) { >> + static_assert(__fn != nullptr); >> + } >> + return _Not_fn_nttp<__fn>{}; >> + } >> + >> +#endif // __cpp_lib_not_fn >= 202306 >> +#endif // __cpp_lib_not_fn >> >> #if __cplusplus >= 201703L >> // Searchers >> 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..feedead477b 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 >> @@ -149,23 +149,23 @@ test03() >> static_assert(is_invocable_r_v<void*, const G4&&>); >> } >> >> -constexpr int f(int i, int j, int k) { return i + 2*(j + k); } >> +constexpr int f(int i, int j, int k) { return i + 2*j + 3*k; } >> >> -constexpr bool >> +constexpr int >> test04() >> { >> auto g = bind_back(f); >> - VERIFY( g(1, 2, 3) == 1 + 2*(2 + 3) ); >> + if (!( g(1, 2, 3) == 1 + 2*2 + 3*3 )) return 7; >> > I do not think you need to replace VERIFY here, > the macro works in they way that VERIFY will fail to be > core-constant expressions if condition is not met, but > will compile fine otherwise. So restore them. > >> auto g1 = bind_back(f, 1); >> - VERIFY( g1(2, 3) == 2 + 2*(3 + 1) ); >> - VERIFY( bind_back(g, 1)(2, 3) == 2 + 2*(3 + 1) ); >> + if (!( g1(2, 3) == 3*1 + 2 + 3*2)) return 6; >> + if (!( bind_back(g, 1)(2, 3) == 3*1 + 2 + 2*3 )) return 5; >> auto g2 = bind_back(f, 1, 2); >> - VERIFY( g2(3) == 3 + 2*(1 + 2) ); >> - VERIFY( bind_back(g1, 2)(3) == 3 + 2*(2 + 1) ); >> + if (!( g2(3) == 3 + 2*1 + 3*2)) return 4; >> + if (!( bind_back(g1, 2)(3) == 3*1 + 2*2 + 3 )) return 3; > > auto g3 = bind_back(f, 1, 2, 3); >> - VERIFY( g3() == 1 + 2*(2 + 3) ); >> - VERIFY( bind_back(g2, 3)() == 3 + 2*(1 + 2) ); >> - return true; >> + if (!( g3() == 1 + 2*2 + 3*3 )) return 2; >> + if (!( bind_back(g2, 3)() == 3*1 + 1*2 + 2*3)) return 1; >> + return 0; >> > After using VERIFY, jus return true here. > >> } >> >> int >> @@ -174,5 +174,5 @@ main() >> test01(); >> test02(); >> test03(); >> - static_assert(test04()); >> + static_assert(test04() == 0); >> > And this will be static_assert(test04()); > >> } >> 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 >> new file mode 100644 >> index 00000000000..23e5fe850c2 >> --- /dev/null >> +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp.cc >> @@ -0,0 +1,224 @@ >> +// { dg-do run { target c++26 } } >> +// { dg-add-options no_pch } >> + >> +// Test NTTP bind_back<f>(Args...), P2714 >> + >> +// #define TRY >> +// #define TRY_CT // turn on static asserts >> + >> +#include <functional> >> + >> +#ifndef __cpp_lib_bind_back >> +# error "Feature test macro for bind_back is missing in <functional>" >> +#elif __cpp_lib_bind_back < 202306L >> +# error "Feature test macro for bind_back has wrong value in >> <functional>" >> +#endif >> + >> +#include <testsuite_hooks.h> >> + >> +using std::bind_back; >> +using std::is_same_v; >> +using std::is_invocable_v; >> +using std::is_invocable_r_v; >> + >> +void >> +test01() >> +{ >> + struct F { void operator()() {} }; >> + constexpr F f{}; >> + >> + // [The following differ from the non-NTTP version of bind_back.] >> > This is a bug in our implementation of bind_back/front. The standard is > clear here: > > BoundArgs is a pack that denotes decay_t<Args>..., > https://eel.is/c++draft/func.bind.partial#1.4: > Could you create a bug in Bugzilla for this? > >> + // Arguments should be decayed: >> +#ifdef TRY_CT >> + // [The following differs from the non-NTTP version of bind_back.] >> + 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&>())) >> + >); >> +#endif >> + >> + // Reference wrappers should be handled: >> + static_assert(!std::is_same_v< >> + decltype(bind_back<f>(std::declval<int&>())), >> + decltype(bind_back<f>(std::ref(std::declval<int&>()))) >> + >); >> + static_assert(!std::is_same_v< >> + decltype(bind_back<f>(std::declval<const int&>())), >> + decltype(bind_back<f>(std::cref(std::declval<int&>()))) >> + >); >> + static_assert(!std::is_same_v< >> + decltype(bind_back<f>(std::ref(std::declval<int&>()))), >> + decltype(bind_back<f>(std::cref(std::declval<int&>()))) >> + >); >> +} >> + >> +void >> +test02() >> +{ >> + struct quals >> + { >> + bool as_const; >> + bool as_lvalue; >> + }; >> + >> + struct F >> + { >> + quals operator()(int) & { return { false, true }; } >> + quals operator()(int) const & { return { true, true }; } >> + quals operator()(int) && { return { false, false }; } >> + quals operator()(int) const && { return { true, false }; } >> + }; >> + >> + { >> + constexpr F f; >> + auto g = bind_back<f>(); >> + const auto& cg = g; >> + quals q; >> + >> + // Constness and value category forwarded to the target object? >> > I have mentioned that in the e-mail, but because f is NTTP, it will be > always > considered to be F const& reference, regardless of the qualifier of the > object. > You should instead adjust the type of argument, as I have mentioned here: > https://gcc.gnu.org/pipermail/libstdc++/2025-July/062426.html > > +#if TRY >> + // [The following differs from the non-NTTP version of bind_back.] >> > + q = g(0); >> + VERIFY( ! q.as_const && q.as_lvalue ); >> + q = std::move(g)(0); >> + VERIFY( ! q.as_const && ! q.as_lvalue ); >> + q = cg(0); >> + VERIFY( q.as_const && q.as_lvalue ); >> + q = std::move(cg)(0); >> + VERIFY( q.as_const && ! q.as_lvalue ); >> +#endif >> + } >> + { >> + constexpr F f; >> + auto g = bind_back<f>(0); >> + const auto& cg = g; >> + quals q; >> + >> + // Constness and value category forwarded to the target object? >> +#ifdef TRY >> + // [The following differs from the non-NTTP version of bind_back.] >> + 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 ); >> +#endif >> + } >> +} >> + >> +void >> +test03() >> +{ >> + struct F >> + { >> + int& operator()(void*, int& i) { return i; } >> + void* operator()(void* p, int) const { return p; } >> + }; >> + >> + int i = 5; >> + void* vp = &vp; // arbitrary void* value >> + >> + constexpr F f; >> + auto g1 = bind_back<f>(i); // call wrapper has bound arg of type int >> + using G1 = decltype(g1); >> + // Invoking G1& will pass g1's bound arg as int&, so calls first >> overload: >> +#ifdef TRY_CT >> + // [The following differs from the non-NTTP version of bind_back.] >> + static_assert(!is_invocable_r_v<int&, G1&, void*>); >> > I have explained the diffs here: > https://gcc.gnu.org/pipermail/libstdc++/2025-July/062426.html > >> +#endif >> + >> + // Invoking const G1& or G&& calls second overload: >> + static_assert(is_invocable_r_v<void*, const G1&, void*>); >> + static_assert(is_invocable_r_v<void*, G1&&, void*>); >> + void* p1 = static_cast<G1&&>(g1)(vp); >> + VERIFY( p1 == vp ); >> + >> + // And can call first overload on const G6: >> + auto g2 = bind_back<f>(std::ref(i)); // bound arg of type int& >> + using G2 = decltype(g2); >> + // Bound arg always forwarded as int& even from G2&& or const G2& >> +#ifdef TRY_CT >> + // [The following differ from the non-NTTP version of bind_back.] >> + static_assert(is_invocable_r_v<int&, G2&, void*>); >> + static_assert(is_invocable_r_v<int&, G2&&, void*>); >> +#endif >> + >> + // But cannot call first overload on const G2: >> +#ifdef TRY_CT >> + // [The following differs from the non-NTTP version of bind_back.] >> + static_assert(is_invocable_r_v<void*, const G2&, void*>); >> + static_assert(is_invocable_r_v<void*, const G2&&, void*>); >> + void* i2 = g2(vp); >> + VERIFY( &i2 == &i ); >> + void* i2r = static_cast<G2&&>(g2)(vp); >> + VERIFY( iv2r == &i ); >> + void* p2 = const_cast<const G2&>(g2)(vp); >> + VERIFY( p2 == vp ); >> +#endif >> + >> + auto g3 = bind_back<f>(std::cref(i)); // bound arg of type const int& >> + using G3 = decltype(g3); >> + // Bound arg always forwarded as const int& so can only call second >> overload: >> + static_assert(is_invocable_r_v<void*, G3&, void*>); >> + static_assert(is_invocable_r_v<void*, G3&&, void*>); >> + static_assert(is_invocable_r_v<void*, const G3&, void*>); >> + static_assert(is_invocable_r_v<void*, const G3&&, void*>); >> + >> +#ifdef TRY_CT >> + // [The following differ from the non-NTTP version of bind_back.] >> + // auto g4 = bind_back<g2>(nullptr); >> + // using G4 = decltype(g4); >> + // static_assert(is_invocable_r_v<int&, G4&>); >> + // static_assert(is_invocable_r_v<int&, G4&&>); >> + // static_assert(is_invocable_r_v<void*, const G4&>); >> + // static_assert(is_invocable_r_v<void*, const G4&&>); >> +#endif >> +} >> + >> +constexpr int f(int i, int j, int k) { return i + 2*j + 3*k; } >> + >> +consteval int >> +test04() >> +{ >> + constexpr auto g = bind_back<f>(); >> + VERIFY( std::is_empty_v<decltype(g)> ); >> > + if (!(g(1, 2, 3) == 1 + 2*2 + 3*3 )) return 7; >> > This could use VERIFY. > >> + constexpr auto g1 = bind_back<f>(1); >> + if (!(g1(2, 3) == 3*1 + 1*2 + 2*3 )) return 6; >> + if (!(bind_back<g>(1)(2, 3) == 3*1 + 1*2 + 2*3 )) return 5; >> + constexpr auto g2 = bind_back<f>(1, 2); >> + if (!(g2(3) == 2*1 + 3*2 + 1*3 )) return 4; >> + if (!(bind_back<g1>(2)(3) == 3*1 + 2*2 + 1*3 )) return 3; >> + constexpr auto g3 = bind_back<f>(1, 2, 3); >> + if (!(g3() == 1 + 2*2 + 3*3)) return 2; >> + if (!(bind_back<g2>(3)() == 1*2 + 2*3 + 3*1 )) return 1; >> + return 0; >> +} >> + >> +template <auto nnfp, auto nfp> >> +void test05() { >> + VERIFY(bind_back<nnfp>(1)(2, 3) == 3*1 + 1*2 + 2*3); >> +#ifdef TRY_CT >> + // [Fails to sandbox the static_assert(fp) in bind_back<fp>():] >> + VERIFY(!requires { bind_back<nfp>(1); }); >> +#endif >> +} >> + >> +int >> +main() >> +{ >> + test01(); >> + test02(); >> + test03(); >> + static_assert(test04() == 0); >> + constexpr int (*nnfp)(int, int, int) = f; >> + constexpr int (*nfp)(int, int, int) = nullptr; >> + test05<nnfp, nfp>(); >> +} >> 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..b038889fbb4 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 >> > Same comments as for bind_back. > >> @@ -149,22 +149,22 @@ test03() >> static_assert(is_invocable_r_v<void*, const G4&&>); >> } >> >> -int f(int i, int j, int k) { return i + j + k; } >> +int f(int i, int j, int k) { return i + 2*j + 3*k; } >> >> void >> test04() >> { >> auto g = bind_front(f); >> - VERIFY( g(1, 2, 3) == 6 ); >> + VERIFY( g(1, 2, 3) == 14 ); >> auto g1 = bind_front(f, 1); >> - VERIFY( g1(2, 3) == 6 ); >> - VERIFY( bind_front(g, 1)(2, 3) == 6 ); >> + VERIFY( g1(2, 3) == 14 ); >> + VERIFY( bind_front(g, 1)(2, 3) == 14 ); >> auto g2 = bind_front(f, 1, 2); >> - VERIFY( g2(3) == 6 ); >> - VERIFY( bind_front(g1, 2)(3) == 6 ); >> + VERIFY( g2(3) == 14 ); >> + VERIFY( bind_front(g1, 2)(3) == 14 ); >> auto g3 = bind_front(f, 1, 2, 3); >> - VERIFY( g3() == 6 ); >> - VERIFY( bind_front(g2, 3)() == 6 ); >> + VERIFY( g3() == 14 ); >> + VERIFY( bind_front(g2, 3)() == 14 ); >> } >> >> int >> 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 >> new file mode 100644 >> index 00000000000..c06b153ee28 >> --- /dev/null >> +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp.cc >> @@ -0,0 +1,218 @@ >> +// { dg-do run { target c++26 } } >> +// { dg-add-options no_pch } >> + >> +// Test NTTP bind_front<f>(Args...), P2714 >> + >> +// #define TRY >> +// #define TRY_CT // turn on static asserts >> + >> +#include <functional> >> + >> +#ifndef __cpp_lib_bind_front >> +# error "Feature test macro for bind_front is missing in <functional>" >> +#elif __cpp_lib_bind_front < 201902L >> +# error "Feature test macro for bind_front has wrong value in >> <functional>" >> +#endif >> + >> +#include <testsuite_hooks.h> >> + >> +using std::bind_front; >> +using std::is_same_v; >> +using std::is_invocable_v; >> +using std::is_invocable_r_v; >> + >> +void >> +test01() >> +{ >> + struct F { void operator()() {} }; >> + constexpr F f{}; >> + >> + // Arguments should be decayed: >> +#ifdef TRY_CT >> + // [The following differ from the non-NTTP version of bind_front.] >> + 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&>())) >> + >); >> +#endif >> + >> + // Reference wrappers should be handled: >> + static_assert(!std::is_same_v< >> + decltype(bind_front<f>(std::declval<int&>())), >> + decltype(bind_front<f>(std::ref(std::declval<int&>()))) >> + >); >> + static_assert(!std::is_same_v< >> + decltype(bind_front<f>(std::declval<const int&>())), >> + decltype(bind_front<f>(std::cref(std::declval<int&>()))) >> + >); >> + static_assert(!std::is_same_v< >> + decltype(bind_front<f>(std::ref(std::declval<int&>()))), >> + decltype(bind_front<f>(std::cref(std::declval<int&>()))) >> + >); >> +} >> + >> +void >> +test02() >> +{ >> + struct quals >> + { >> + bool as_const; >> + bool as_lvalue; >> + }; >> + >> + struct F >> + { >> + quals operator()(int) & { return { false, true }; } >> + quals operator()(int) const & { return { true, true }; } >> + quals operator()(int) && { return { false, false }; } >> + quals operator()(int) const && { return { true, false }; } >> + }; >> + >> + { >> + constexpr F f; >> + auto g = bind_front<f>(); >> + const auto& cg = g; >> + quals q; >> + >> + // Constness and value category forwarded to the target object? >> +#ifdef TRY >> + // [The following differ from the non-NTTP version of bind_front.] >> + q = g(0); >> + VERIFY( ! q.as_const && q.as_lvalue ); >> + q = std::move(g)(0); >> + VERIFY( ! q.as_const && ! q.as_lvalue ); >> + q = cg(0); >> + VERIFY( q.as_const && q.as_lvalue ); >> + q = std::move(cg)(0); >> + VERIFY( q.as_const && ! q.as_lvalue ); >> +#endif >> + } >> + { >> + constexpr F f; >> + auto g = bind_front<f>(0); >> + const auto& cg = g; >> + quals q; >> + >> + // Constness and value category forwarded to the target object? >> +#ifdef TRY >> + // [The following differ from the non-NTTP version of bind_front.] >> + 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 ); >> +#endif >> + } >> +} >> + >> +void >> +test03() >> +{ >> + struct F >> + { >> + int& operator()(int& i, void*) { return i; } >> + void* operator()(int, void* p) const { return p; } >> + }; >> + >> + int i = 5; >> + void* vp = &vp; // arbitrary void* value >> + >> + constexpr F f; >> + auto g1 = bind_front<f>(i); // call wrapper has bound arg of type int >> + using G1 = decltype(g1); >> + // Invoking G1& will pass g1's bound arg as int&, so calls first >> overload: >> +#ifdef TRY_CT >> + // [The following differs from the non-NTTP version of bind_front.] >> + static_assert(is_invocable_r_v<int&, G1&, void*>); >> +#endif >> + >> + // Invoking const G1& or G&& calls second overload: >> + static_assert(is_invocable_r_v<void*, const G1&, void*>); >> + static_assert(is_invocable_r_v<void*, G1&&, void*>); >> + void* p1 = static_cast<G1&&>(g1)(vp); >> + VERIFY( p1 == vp ); >> + >> + // And can call first overload on const G6: >> + auto g2 = bind_front<f>(std::ref(i)); // bound arg of type int& >> + using G2 = decltype(g2); >> + // Bound arg always forwarded as int& even from G2&& or const G2& >> +#ifdef TRY_CT >> + // [The following differ from the non-NTTP version of bind_front.] >> + static_assert(is_invocable_r_v<int&, G2&, void*>); >> + static_assert(is_invocable_r_v<int&, G2&&, void*>); >> + // But cannot call first overload on const G2: >> + static_assert(is_invocable_r_v<void*, const G2&, void*>); >> + static_assert(is_invocable_r_v<void*, const G2&&, void*>); >> + int& i2 = g2(vp); >> + VERIFY( &i2 == &i ); >> + int& i2r = static_cast<G2&&>(g2)(vp); >> + VERIFY( &i2r == &i ); >> + void* p2 = const_cast<const G2&>(g2)(vp); >> + VERIFY( p2 == vp ); >> +#endif >> + >> + auto g3 = bind_front<f>(std::cref(i)); // bound arg of type const int& >> + using G3 = decltype(g3); >> + // Bound arg always forwarded as const int& so can only call second >> overload: >> + static_assert(is_invocable_r_v<void*, G3&, void*>); >> + static_assert(is_invocable_r_v<void*, G3&&, void*>); >> + static_assert(is_invocable_r_v<void*, const G3&, void*>); >> + static_assert(is_invocable_r_v<void*, const G3&&, void*>); >> + >> +#ifdef TRY_CT >> + // [The following differ from the non-NTTP version of bind_front.] >> + auto g4 = bind_front<g2>(nullptr); >> + using G4 = decltype(g4); >> + static_assert(is_invocable_r_v<int&, G4&>); >> + static_assert(is_invocable_r_v<int&, G4&&>); >> + static_assert(is_invocable_r_v<void*, const G4&>); >> + static_assert(is_invocable_r_v<void*, const G4&&>); >> +#endif >> +} >> + >> +constexpr int f(int i, int j, int k) { return i + 2*j + 3*k; } >> + >> +consteval int >> +test04() >> +{ >> + constexpr auto g = bind_front<f>(); >> + VERIFY( std::is_empty_v<decltype(g)> ); >> + if (!(g(1, 2, 3) == 1 + 2*2 + 3*3 )) return 7; >> + constexpr auto g1 = bind_front<f>(1); >> + if (!(g1(2, 3) == 1 + 2*2 + 3*3 )) return 6; >> + if (!(bind_front<g>(1)(2, 3) == 1 + 2*2 + 3*3 )) return 5; >> + constexpr auto g2 = bind_front<f>(1, 2); >> + if (!(g2(3) == 1 + 2*2 + 3*3 )) return 4; >> + if (!(bind_front<g1>(2)(3) == 1 + 2*2 + 3*3 )) return 3; >> + constexpr auto g3 = bind_front<f>(1, 2, 3); >> + if (!(g3() == 1 + 2*2 + 3*3)) return 2; >> + if (!(bind_front<g2>(3)() == 1 + 2*2 + 3*3 )) return 1; >> + return 0; >> +} >> + >> +template <auto nnfp, auto nfp> >> +constexpr void test05() { >> + VERIFY(bind_front<nnfp>(1)(2, 3) == 14); >> +#ifdef TRY_CT // Fails to sandbox the static_assert(fp) in >> bind_front<fp>(): >> + VERIFY(!requires { bind_front<nfp>(1); }); >> +#endif >> +} >> + >> +int >> +main() >> +{ >> + test01(); >> + test02(); >> + test03(); >> + static_assert(test04() == 0); >> + constexpr int (*nnfp)(int, int, int) = f; >> + constexpr int (*nfp)(int, int, int) = nullptr; >> + test05<nnfp, nfp>(); >> +} >> diff --git >> a/libstdc++-v3/testsuite/20_util/function_objects/not_fn/nttp.cc >> b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/nttp.cc >> new file mode 100644 >> index 00000000000..b9a74bd16c6 >> --- /dev/null >> +++ b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/nttp.cc >> @@ -0,0 +1,113 @@ >> +// Test NTTP version of not_fn, from P2714 >> + >> +// { dg-do run { target c++26 } } >> + >> +// #define TRY >> +// #define TRY_CT // turn on failing static asserts >> + >> +#ifndef __cpp_lib_bind_back >> +# error "Feature test macro for bind_back is missing in <functional>" >> +#elif __cpp_lib_bind_back < 202306L >> +# error "Feature test macro for bind_back has wrong value in >> <functional>" >> +#endif >> + >> +#include <functional> >> +#include <testsuite_hooks.h> >> + >> +using std::not_fn; >> + >> +int func(int, char) { return 0; } >> + >> +struct F >> +{ >> + bool operator()() { return false; } >> + bool operator()() const { return true; } >> + bool operator()(int) const { return false; } >> +}; >> + >> +void >> +test01() >> +{ >> + auto f1 = not_fn<func>(); >> + VERIFY( std::is_empty_v<decltype(f1)> ); >> + VERIFY( f1(1, '2') == true ); >> + >> + auto f2 = not_fn<[] { return true; }>(); >> + VERIFY( std::is_empty_v<decltype(f2)> ); >> + VERIFY( f2() == false ); >> + >> + auto f3 = not_fn<F{}>(); >> + VERIFY( f3() == false ); // Prefer the const member. >> + VERIFY( f3(1) == true ); >> + const auto f4 = f3; >> + VERIFY( f4() == false ); >> +} >> + >> +void >> +test04() >> +{ >> + struct abstract { virtual void f() = 0; }; >> + struct derived : abstract { void f() { } }; >> + struct F { bool operator()(abstract&) const { return false; } }; >> + constexpr F f; >> + derived d; >> + VERIFY( not_fn<f>()(d) ); >> +} >> + >> +void >> +test05() >> +{ >> + auto nf = std::not_fn<[] { return false; }>(); >> + auto copy(nf); // PR libstdc++/70564 >> +} >> + >> +void >> +test06() >> +{ >> + struct Boolean { >> + Boolean operator!() noexcept(false) { return *this; } >> + }; >> + struct F { >> + Boolean operator()() { return {}; } >> + }; >> + F f; >> + auto notf = std::not_fn<f>(); >> + using NotF = decltype(notf); >> + >> + // [This fails for NTTP not_fn. ] >> +#ifdef TRY_CT >> + static_assert( std::is_invocable<NotF>::value, "cannot negate" ); >> +#endif >> + >> + // [This check fails for NTTP not_fn, even though actually invoking it >> fails. ] >> +#ifdef TRY_CT >> + static_assert( !noexcept(notf()), "conversion to bool affects >> noexcept" ); >> +#endif >> +} >> + >> +void >> +test07() >> +{ >> + struct NonNegatable { }; // there is no operator!(NonNegatable) >> + struct F { >> + NonNegatable operator()() const { return {}; } >> + }; >> + F f; >> + constexpr auto notf = std::not_fn<f>(); >> + using NotF = decltype(notf); >> + >> +#ifdef TRY_CT >> + // [This check fails for NTTP not_fn, even though actually invoking it >> fails. ] >> + static_assert( !std::is_invocable<NotF>::value, "cannot negate" ); >> +#endif >> +} >> + >> +int >> +main() >> +{ >> + test01(); >> + test04(); >> + test05(); >> + test06(); >> + test07(); >> +} >> diff --git >> a/libstdc++-v3/testsuite/20_util/headers/functional/synopsis.cc >> b/libstdc++-v3/testsuite/20_util/headers/functional/synopsis.cc >> index e3e92076f5c..5e835d684fd 100644 >> --- a/libstdc++-v3/testsuite/20_util/headers/functional/synopsis.cc >> +++ b/libstdc++-v3/testsuite/20_util/headers/functional/synopsis.cc >> @@ -57,6 +57,13 @@ namespace std { >> template <class Predicate> >> _GLIBCXX14_CONSTEXPR >> binary_negate<Predicate> not2(const Predicate&); >> +#ifdef __cpp_lib_not_fn >> + template <typename F> _GLIBCXX20_CONSTEXPR auto not_fn(F&&) >> + noexcept(std::is_nothrow_constructible<std::decay_t<F>, F&&>::value); >> +#if __cpp_lib_not_fn >= 2020306 >> + template <auto f> constexpr auto not_fn() noexcept; >> +#endif >> +#endif >> >> // lib.binders, binders: >> template <class Operation> class binder1st; >> @@ -65,6 +72,20 @@ namespace std { >> template <class Operation> class binder2nd; >> template <class Operation, class T> >> binder2nd<Operation> bind2nd(const Operation&, const T&); >> +#ifdef __cpp_lib_bind_front >> + template <typename F, typename... Args> >> + _GLIBCXX20_CONSTEXPR auto bind_front(F&&, Args&&...); >> +#if __cpp_lib_bind_front >= 202306 >> + template <auto f, typename... Args> constexpr auto >> bind_front(Args&&...); >> +#endif >> +#endif >> +#ifdef __cpp_lib_bind_back >> + template <typename F, typename... Args> >> + _GLIBCXX20_CONSTEXPR auto bind_back(F&&, Args&&...); >> +#if __cpp_lib_bind_back >= 202306 >> + template <auto f, typename... Args> constexpr auto >> bind_back(Args&&...); >> +#endif >> +#endif >> >> // lib.function.pointer.adaptors, adaptors: >> template <class Arg, class Result> class pointer_to_unary_function; >> -- >> 2.50.0 >> >>