On Fri, Jul 18, 2025 at 12:52 PM Nathan Myers <n...@cantrip.org> wrote:
> * I rewrote the implementation to more closely resemble the > non-NTTP version, and fix the various noexcept and requires > omissions. > I have a task to adjust them later, so the alignment may not be desired. > > * I don't understand what "a separate type for single argument > case, that correspond to function_ref nontype, ptr/ref constructor" > means, but I did make no-bound-arguments overloads. > I was thinking about having: template<auto __fn> struct _Fn_t // No bound argument, could be used for both bind_front and bind_back. { template<typename... Argos> static operator()(_Args&&... args); }; template<auto __fn, typename T> struct _Bind_front1_t // Only one bind argument { T arg; template<typename... Argos> static operator()(_Args&&... args); // delete assignments, but keep copy and move }; // The function_ref has (non_type<f>, ref) that does not dangle, // then we could also make _Bind_front_1 to do the same. And then having in bind_front() if constexpr (sizeof(_BoundsArgs) == 0) return _Fn_t<__fn>(); else if constexpr (sizeof(_BoundsArgs) == 1) return _Bind_front1_t<__fn>(); else // use lambda, to avoid instantiating tuple Similary bind_back, could use __Fn_t. > > * P2714R1 has "Mandates: If is_pointer_v<F> || is_member_pointer_v<F> > is true, then f != nullptr is true." This seems to require the > "if constexpr" statements as written. > Thanks, I missed that. Indeed this is correct. > > * Some of the tests have results that differ from the non-NTTP > results. Some differences are a consequence of different const > qualification, as the argument callable is necessarily const. > Others I don't understand, e.g. std::invocable_v reporting a call > would work, where actually invoking it fails. Differences from > the non-NTTP */1.cc tests are commented in */nttp.cc . > > 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 | 230 +++++++++++++++++- > .../20_util/function_objects/bind_back/1.cc | 22 +- > .../function_objects/bind_back/nttp.cc | 191 +++++++++++++++ > .../20_util/function_objects/bind_front/1.cc | 16 +- > .../function_objects/bind_front/nttp.cc | 188 ++++++++++++++ > .../20_util/function_objects/not_fn/nttp.cc | 101 ++++++++ > .../20_util/headers/functional/synopsis.cc | 21 ++ > 9 files changed, 778 insertions(+), 24 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..37c4193bdb2 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 > @@ -940,7 +941,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 +1050,92 @@ _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 > + > + template<auto __f, typename... _BoundArgs> > + struct _Bind_front_nttp > + { > + static_assert((is_move_constructible_v<_BoundArgs> && ...)); > + > + using _Fn = decltype(__f); > + using _BoundIndices = index_sequence_for<_BoundArgs...>; > + > + // First parameter ensures this constructor is not confused > + // with a copy or move constructor. > + template<typename... _FwdArgs> // derived from _BoundArgs > + constexpr explicit > + _Bind_front_nttp(int, _FwdArgs&&... __args) > + noexcept(__and_<is_nothrow_constructible<_BoundArgs, _FwdArgs>... > + >::value) > + : _M_bound_args(std::forward<_FwdArgs>(__args)...) > + { static_assert(sizeof...(_FwdArgs) == sizeof...(_BoundArgs)); } > + > + template<typename _Self, typename... _CallArgs> > + constexpr auto > This should be decltype(auto), we need to preserve reference returns. > + operator()(this _Self&& __self, _CallArgs&&... __call_args) > + noexcept(is_nothrow_invocable_v< > + _Fn, __like_t<_Self, _BoundArgs>..., _CallArgs...>) > + requires (is_invocable_v< > + _Fn, __like_t<_Self, _BoundArgs>..., _CallArgs...>) > + { > + return _S_call(__like_t<_Self, _Bind_front_nttp>(__self), > + _BoundIndices(), std::forward<_CallArgs>(__call_args)...); > + } > + > + private: > + > + template<typename _Self, size_t... _Ind, typename... _CallArgs> > + constexpr static > + decltype(auto) > + _S_call(_Self&& __g, index_sequence<_Ind...>, _CallArgs&&... > __call_args) > + noexcept(is_nothrow_invocable_v< > + _Fn, __like_t<_Self, _BoundArgs>..., _CallArgs...>) > + { > + return std::invoke(__f, > + std::get<_Ind>(std::forward<_Self>(__g)._M_bound_args)..., > + std::forward<_CallArgs>(__call_args)...); > + } > + > + [[no_unique_address]] std::tuple<_BoundArgs...> _M_bound_args; > + }; > + > + /** Create call wrapper by partial application of arguments to function. > + * > + * The result of `std::bind_front<f>(args...)` is a function object that > + * stores the bound arguments, `args...`. When that function object is > + * invoked with `call_args...` it returns the result of calling > + * `f(args..., call_args...)`. > + * > + * @since C++26 > + */ > + template<auto __fn> > + constexpr auto > + bind_front() > + noexcept > + { > + using _Fn = decltype(__fn); > + if constexpr (is_pointer_v<_Fn> or is_member_pointer_v<_Fn>) { > + static_assert(__fn != nullptr); > + } > + return __fn; > + } > + > + template<auto __fn, typename... _Args> > + constexpr auto > + bind_front(_Args&&... __args) > + noexcept(__and_<is_nothrow_constructible<_Args>...>::value) > + requires ( > + (is_constructible_v<decay_t<_Args>, _Args> and ...) and > + (is_move_constructible_v<decay_t<_Args>> and ...)) > + { > + using _Fn = decltype(__fn); > + if constexpr (is_pointer_v<_Fn> or is_member_pointer_v<_Fn>) { > + static_assert(__fn != nullptr); > + } > + return _Bind_front_nttp<__fn, _Args...>(0, > std::forward<_Args>(__args)...); > + } > +#endif // __cpp_lib_bind_front // C++26 > #endif // __cpp_lib_bind_front > > #ifdef __cpp_lib_bind_back // C++ >= 23 > @@ -1118,6 +1205,92 @@ _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 > + > + template<auto __f, typename... _BoundArgs> > + struct _Bind_back_nttp > + { > + static_assert((is_move_constructible_v<_BoundArgs> && ...)); > + > + using _Fn = decltype(__f); > + using _BoundIndices = index_sequence_for<_BoundArgs...>; > + > + // First parameter ensures this constructor is not confused > + // with a copy or move constructor. > + template<typename... _FwdArgs> // derived from _BoundArgs > + constexpr explicit > + _Bind_back_nttp(int, _FwdArgs&&... __args) > + noexcept(__and_<is_nothrow_constructible<_BoundArgs, _FwdArgs>... > + >::value) > + : _M_bound_args(std::forward<_FwdArgs>(__args)...) > + { static_assert(sizeof...(_FwdArgs) == sizeof...(_BoundArgs)); } > + > + template<typename _Self, typename... _CallArgs> > + constexpr auto > + operator()(this _Self&& __self, _CallArgs&&... __call_args) > + noexcept(is_nothrow_invocable_v< > + _Fn, _CallArgs..., __like_t<_Self, _BoundArgs>...>) > + requires (is_invocable_v< > + _Fn, _CallArgs..., __like_t<_Self, _BoundArgs>...>) > + { > + return _S_call(__like_t<_Self, _Bind_back_nttp>(__self), > + _BoundIndices(), std::forward<_CallArgs>(__call_args)...); > + } > + > + private: > + > + template<typename _Self, size_t... _Ind, typename... _CallArgs> > + constexpr static > + decltype(auto) > + _S_call(_Self&& __g, index_sequence<_Ind...>, _CallArgs&&... > __call_args) > + noexcept(is_nothrow_invocable_v< > + _Fn, _CallArgs..., __like_t<_Self, _BoundArgs>...>) > + { > + return std::invoke(__f, > + std::forward<_CallArgs>(__call_args)..., > + std::get<_Ind>(std::forward<_Self>(__g)._M_bound_args)...); > + } > + > + [[no_unique_address]] std::tuple<_BoundArgs...> _M_bound_args; > + }; > + > + /** Create call wrapper by partial application of arguments to function. > + * > + * The result of `std::bind_back<f>(args...)` is a function object that > + * stores the bound arguments, `args...`. When that function object is > + * invoked with `call_args...` it returns the result of calling > + * `f(call_args..., args...)`. > + * > + * @since C++26 > + */ > + template<auto __fn> > + constexpr auto > + bind_back() > + noexcept > + { > + using _Fn = decltype(__fn); > + if constexpr (is_pointer_v<_Fn> or is_member_pointer_v<_Fn>) { > + static_assert(__fn != nullptr); > + } > + return __fn; > + } > + > + template<auto __fn, typename... _Args> > + constexpr auto > + bind_back(_Args&& ...__args) > + noexcept(__and_<is_nothrow_constructible<_Args>...>::value) > + requires ( > + (is_constructible_v<decay_t<_Args>, _Args> and ...) and > + (is_move_constructible_v<decay_t<_Args>> and ...)) > + { > + using _Fn = decltype(__fn); > + if constexpr (is_pointer_v<_Fn> or is_member_pointer_v<_Fn>) { > + static_assert(__fn != nullptr); > + } > + return _Bind_back_nttp<__fn, _Args...>(0, > std::forward<_Args>(__args)...); > + } > +#endif // __cpp_lib_bind_back // C++26, nttp > #endif // __cpp_lib_bind_back > > #if __cplusplus >= 201402L > @@ -1218,7 +1391,60 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > { > return _Not_fn<std::decay_t<_Fn>>{std::forward<_Fn>(__fn), 0}; > } > -#endif > + > +#if __cpp_lib_not_fn >= 202306 > + /// Generalized negator, empty object. > + template<auto __fn> > + class _Not_fn_nttp > + { > + using _Fn = decltype(__fn); > + template<typename _Tp> > + constexpr static > + decltype(! declval<_Tp>()) > + _S_not(_Tp&&) noexcept(noexcept(! declval<_Tp>())); > + > + public: > + template<typename... _Args> > + constexpr static auto > Again decltype(auto). > + operator()(_Args&&... __args) > I think it may be just simpler for you to do: requires requires { !std::invoke(__fn, std::forward<_Args>(__args)...) } noexcept(noexcept(!std::invoke(__fn, std::forward<_Args>(__args)...)) > + noexcept( > + is_nothrow_invocable_v<_Fn, _Args...> and > + noexcept(_S_not<invoke_result_t<_Fn, _Args...>>( > + declval<invoke_result_t<_Fn, _Args...>>()) )) > + requires ( > + is_invocable_v<_Fn, _Args...> and > + is_invocable_v< > + decltype(_S_not<invoke_result_t<_Fn, _Args...>>), > + invoke_result_t<_Fn, _Args...> >) > + { > + return ! 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 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; > 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; > } > > int > @@ -174,5 +174,5 @@ main() > test01(); > test02(); > test03(); > - static_assert(test04()); > + static_assert(test04() == 0); > } > 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..467502c7473 > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp.cc > @@ -0,0 +1,191 @@ > +// { dg-do run { target c++26 } } > +// { dg-add-options no_pch } > + > +// Test NTTP bind_back<f>(Args...), P2714 > + > +#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.] > + // 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&>())) > + // >); > + > + // 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()() & { return { false, true }; } > + quals operator()() const & { return { true, true }; } > + quals operator()() && { return { false, false }; } > + quals operator()() 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? > + 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 ); > +} > + > +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: > + // [The following differs from the non-NTTP version of bind_back.] > + static_assert(!is_invocable_r_v<int&, G1&, void*>); > + > + // 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& > + // [The following differs 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*>); > + > + // But cannot call first overload on const G2: > + // [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*>); > + // [The following differ from the non-NTTP version of bind_back.] > + //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 ); > + > + 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*>); > + > + // [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&&>); > +} > + > +constexpr int f(int i, int j, int k) { return i + 2*j + 3*k; } > + > +consteval int > +test04() > +{ > + constexpr auto g = bind_back<f>(); > + if (!(g(1, 2, 3) == 1 + 2*2 + 3*3 )) return 7; > + 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; > + > + // [The following differs from the non-NTTP version of bind_back.] > + // 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; > + > + // [The following differs from the non-NTTP version of bind_back.] > + // 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); > +#if 0 // 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 > @@ -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..a16adfbd379 > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp.cc > @@ -0,0 +1,188 @@ > +// { dg-do run { target c++26 } } > +// { dg-add-options no_pch } > + > +// Test NTTP bind_front<f>(Args...), P2714 > + > +#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{}; > + > + // [The following differ from the non-NTTP version of bind_front.] > + // 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&>())) > + // >); > + > + // 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()() & { return { false, true }; } > + quals operator()() const & { return { true, true }; } > + quals operator()() && { return { false, false }; } > + quals operator()() 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? > + 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 ); > +} > + > +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: > + // [The following differs from the non-NTTP version of bind_front.] > + // static_assert(is_invocable_r_v<int&, G1&, void*>); > + > + // 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& > + // static_assert(is_invocable_r_v<int&, G2&, void*>); > + // static_assert(is_invocable_r_v<int&, G2&&, void*>); > + > + // [The following differ from the non-NTTP version of bind_front.] > + // 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 ); > + > + 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*>); > + > + // [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&&>); > +} > + > +constexpr int f(int i, int j, int k) { return i + 2*j + 3*k; } > + > +consteval int > +test04() > +{ > + constexpr auto g = bind_front<f>(); > + 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; > + > + // [The following differ from the non-NTTP version of bind_front.] > + // 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; > + > + // [The following differ from the non-NTTP version of bind_front.] > + // 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); > +#if 0 // 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..b7c06c103a7 > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/nttp.cc > @@ -0,0 +1,101 @@ > +// Test NTTP version of not_fn, from P2714 > + > +// { dg-do run { target c++26 } } > + > +#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( f1(1, '2') == true ); > + > + auto f2 = not_fn<[] { return true; }>(); > + 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 check fails for NTTP not_fn, even though actually invoking it > fails. ] > + // static_assert( std::is_invocable<NotF>::value, "cannot negate" ); > + // > + static_assert( !noexcept(notf()), "conversion to bool affects noexcept" > ); > +} > + > +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); > + > + // [This check fails for NTTP not_fn, even though actually invoking it > fails. ] > + // static_assert( !std::is_invocable<NotF>::value, "cannot negate" ); > +} > + > +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 > >