[Note: many of the tests for the original versions do not make sense, require different outcomes, or otherwise fail on the new versions for reasons not obvious.]
Add non-type template parameter function-object/-pointer argument versions of bind_front, bind_back, and not_fn as defined in P2714R1. 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 | 124 +++++++++++- .../20_util/function_objects/bind_back/1.cc | 22 +-- .../function_objects/bind_back/nttp.cc | 186 ++++++++++++++++++ .../20_util/function_objects/bind_front/1.cc | 16 +- .../function_objects/bind_front/nttp.cc | 185 +++++++++++++++++ .../20_util/function_objects/not_fn/nttp.cc | 97 +++++++++ .../20_util/headers/functional/synopsis.cc | 22 +++ 9 files changed, 662 insertions(+), 23 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 5d5758bf203..8ab9a7207e7 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 2b00e8419b3..c204ae3c48c 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..21f0b1cb2d5 100644 --- a/libstdc++-v3/include/std/functional +++ b/libstdc++-v3/include/std/functional @@ -940,7 +940,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...> @@ -1218,8 +1218,130 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return _Not_fn<std::decay_t<_Fn>>{std::forward<_Fn>(__fn), 0}; } +#if __cpp_lib_not_fn >= 202306 + /** 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 []<typename... _Tp> (_Tp&&... __call_args) + noexcept(is_nothrow_invocable_v<_Fn, _Tp...>) + -> invoke_result_t<_Fn, _Tp...> + { + return !invoke(__fn, forward<_Tp>(__call_args)...); + }; + } +#endif // __cpp_lib_not_fn >= 202306 +#endif // __cpp_lib_not_fn + +#if __cpp_lib_bind_front >= 202306 || __cpp_lib_bind_back >= 202306 + + template<typename _Tp, typename _Up> + using __copy_const = conditional<is_const_v<_Tp>, const _Up, _Up>; + + template<typename _Tp, typename _Up, + typename _Xp = __copy_const<remove_reference_t<_Tp>, _Up>::type> + using __copy_value_category = + conditional<is_lvalue_reference_v<_Tp&&>, _Xp&, _Xp&&>; + + template<typename _Tp, typename _Up> + using __type_forward_like = + __copy_value_category<_Tp, remove_reference_t<_Up>>; + + template<typename _Tp, typename _Up> + using __type_forward_like_t = __type_forward_like<_Tp, _Up>::type; + #endif +#if __cpp_lib_bind_front >= 202306 + /** 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, typename... _Args> + constexpr auto + bind_front(_Args&&... __args) + 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 [... __bound_args(std::forward<_Args>(__args))]< + typename _Self, typename... _Tp> + (this _Self&&, _Tp&&... __call_args) + noexcept(is_nothrow_invocable_v< + _Fn, __type_forward_like_t<_Self, decay_t<_Args>>..., _Tp...>) + -> invoke_result_t< + _Fn, __type_forward_like_t<_Self, decay_t<_Args>>..., _Tp...> + { + return invoke(__fn, + forward_like<_Self>(__bound_args)..., + forward<_Tp>(__call_args)...); + }; + } +#endif // __cpp_lib_bind_front + +#if __cpp_lib_bind_back >= 202306 + /** 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, typename... _Args> + constexpr auto + bind_back(_Args&&... __args) + 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 [... __bound_args(std::forward<_Args>(__args))]< + typename _Self, typename... _Tp> + (this _Self&&, _Tp&&... __call_args) + noexcept(is_nothrow_invocable_v< + _Fn, _Tp..., __type_forward_like_t<_Self, decay_t<_Args>>...>) + -> invoke_result_t< + _Fn, _Tp..., __type_forward_like_t<_Self, decay_t<_Args>>...> + { + return invoke(__fn, + forward<_Tp>(__call_args)..., + forward_like<_Self>(__bound_args)...); + }; + } +#endif // __cpp_lib_bind_back + #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..d75f678fee3 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp.cc @@ -0,0 +1,186 @@ +// { 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()() {} }; + const 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? + // [This behavior differs from the non-NTTP bind_back: + // All uses call operator() const&.] + q = g(); + // VERIFY( ! q.as_const && q.as_lvalue ); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(g)(); + // VERIFY( ! q.as_const && ! q.as_lvalue ); + 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 ); + 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 + + 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: + 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& + 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_back.] + // 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_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; } + +constexpr 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; + 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); +#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..39465ef7cc1 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp.cc @@ -0,0 +1,185 @@ +// { 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()() {} }; + const 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? + // [This behavior differs from the non-NTTP bind_front: + // All uses call operator() const&.] + q = g(); + // VERIFY( ! q.as_const && q.as_lvalue ); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(g)(); + // VERIFY( ! q.as_const && ! q.as_lvalue ); + 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 ); + 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 + + 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: + 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; } + +void +test04() +{ + constexpr auto g = bind_front<f>(); + VERIFY( g(1, 2, 3) == 14 ); + constexpr auto g1 = bind_front<f>(1); + VERIFY( g1(2, 3) == 14 ); + VERIFY( bind_front<g>(1)(2, 3) == 14 ); + constexpr auto g2 = bind_front<f>(1, 2); + VERIFY( g2(3) == 14 ); + VERIFY( bind_front<g1>(2)(3) == 14 ); + constexpr auto g3 = bind_front<f>(1, 2, 3); + VERIFY( g3() == 14 ); + VERIFY( bind_front<g2>(3)() == 14 ); +} + +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(); + test04(); + 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..f08e48f4157 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/nttp.cc @@ -0,0 +1,97 @@ +// 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); + 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 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..0af3504dc3a 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; @@ -102,4 +123,5 @@ namespace std { const_mem_fun_ref_t<S,T> mem_fun_ref(S (T::*f)() const); template <class S, class T, class A> const_mem_fun1_ref_t<S,T,A> mem_fun_ref(S (T::*f)(A) const); + } -- 2.50.0