+ {
+ using _Fn = decltype(__fn);
+ static_assert(
+ (is_constructible_v<decay_t<_BindArgs>, _BindArgs> && ...) &&
+ (is_move_constructible_v<decay_t<_BindArgs>> && ...));
+ if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>)
+ static_assert(__fn != nullptr);
+
+ if constexpr (sizeof...(_BindArgs) == 0)
+ return _Bind_fn_t<__fn>{};
+ else {
+ return [... __bound_args(std::forward<_BindArgs>(__bind_args))]
+ <typename _Self, typename... _CallArgs>
+ (this _Self&&, _CallArgs&&... __call_args)
+ noexcept(is_nothrow_invocable_v<
+ const _Fn&, __like_t<_Self, decay_t<_BindArgs>>..., _CallArgs...>)
+ -> decltype(auto)
+ requires is_invocable_v<
+ const _Fn&, __like_t<_Self, decay_t<_BindArgs>>..., _CallArgs...>
+ {
+ return std::invoke(__fn,
+ std::forward_like<_Self>(__bound_args)...,
+ std::forward<_CallArgs>(__call_args)...);
+ };
+ }
+ }
+
+#endif // __cpp_lib_bind_front // C++26
#endif // __cpp_lib_bind_front
#ifdef __cpp_lib_bind_back // C++ >= 23
@@ -1118,6 +1178,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)
+ {
+ using _Fn = decltype(__fn);
+ static_assert(
+ (is_constructible_v<decay_t<_BindArgs>, _BindArgs> && ...) &&
+ (is_move_constructible_v<decay_t<_BindArgs>> && ...));
+ if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>)
+ static_assert(__fn != nullptr);
+
+ if constexpr (sizeof...(_BindArgs) == 0)
+ return _Bind_fn_t<__fn>{};
+ else
+ {
+ // Capture arguments in a lambda and return that.
+ return [... __bound_args(std::forward<_BindArgs>(__bind_args))]
+ <typename _Self, typename... _CallArgs>
+ (this _Self&&, _CallArgs&&... __call_args)
+ noexcept(is_nothrow_invocable_v<
+ const _Fn&, _CallArgs..., __like_t<_Self, decay_t<_BindArgs>>...>)
+ -> decltype(auto)
+ requires is_invocable_v<
+ const _Fn&, _CallArgs..., __like_t<_Self, decay_t<_BindArgs>>...>
+ {
+ return std::invoke(__fn,
+ std::forward<_CallArgs>(__call_args)...,
+ std::forward_like<_Self>(__bound_args)...);
+ };
+ }
+ }
+
+#endif // __cpp_lib_bind_back // C++26, nttp
#endif // __cpp_lib_bind_back
#if __cplusplus >= 201402L
@@ -1218,7 +1324,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{
return _Not_fn<std::decay_t<_Fn>>{std::forward<_Fn>(__fn), 0};
}
-#endif
+
+#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 decltype(auto)
+ not_fn() noexcept
+ {
+ using _Fn = decltype(__fn);
+ if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>)
+ static_assert(__fn != nullptr);
+ return []<typename... _Args>(_Args... __args) static
+ noexcept(noexcept(
+ !std::invoke(__fn, std::forward<_Args>(__args)...) ))
+ -> decltype(auto)
+ requires requires {
+ !std::invoke(__fn, std::forward<_Args>(__args)...); }
+ { return !std::invoke(__fn, std::forward<_Args>(__args)...); };
+ };
+
+#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..f3559b7f960 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,22 +149,22 @@ 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
test04()
{
auto g = bind_back(f);
- VERIFY( g(1, 2, 3) == 1 + 2*(2 + 3) );
+ VERIFY( g(1, 2, 3) == 1 + 2*2 + 3*3 );
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) );
+ VERIFY( g1(2, 3) == 3*1 + 2 + 3*2);
+ VERIFY( bind_back(g, 1)(2, 3) == 3*1 + 2 + 2*3 );
auto g2 = bind_back(f, 1, 2);
- VERIFY( g2(3) == 3 + 2*(1 + 2) );
- VERIFY( bind_back(g1, 2)(3) == 3 + 2*(2 + 1) );
+ VERIFY( g2(3) == 3 + 2*1 + 3*2);
+ VERIFY( bind_back(g1, 2)(3) == 3*1 + 2*2 + 3 );
auto g3 = bind_back(f, 1, 2, 3);
- VERIFY( g3() == 1 + 2*(2 + 3) );
- VERIFY( bind_back(g2, 3)() == 3 + 2*(1 + 2) );
+ VERIFY( g3() == 1 + 2*2 + 3*3 );
+ VERIFY( bind_back(g2, 3)() == 3*1 + 1*2 + 2*3);
return true;
}
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..4dff909a387
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp.cc
@@ -0,0 +1,258 @@
+// { 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()(int) {} };
+ constexpr F f{};
+
+ // 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, int) & { return { false, true }; }
+ quals operator()(int, int) const & { return { true, true }; }
+ quals operator()(int, int) && { return { false, false }; }
+ quals operator()(int, int) const && { return { true, false }; }
+ };
+
+ // Constness and value category forwarded to the target object?
+ { // no bound args
+ constexpr F f;
+ auto g = bind_back<f>();
+ const auto& cg = g;
+ quals q;
+
+ q = g(0,0);
+ VERIFY( q.as_const && q.as_lvalue );
+ q = std::move(g)(0,0);
+ VERIFY( q.as_const && q.as_lvalue );
+ q = cg(0,0);
+ VERIFY( q.as_const && q.as_lvalue );
+ q = std::move(cg)(0,0);
+ VERIFY( q.as_const && q.as_lvalue );
+ }
+ { // one bound arg
+ constexpr F f;
+ auto g = bind_back<f>(0);
+ const auto& cg = g;
+ quals q;
+
+ 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 );
+ }
+ { // two bound args, the general case
+ constexpr F f;
+ auto g = bind_back<f>(0,0);
+ const auto& cg = g;
+ quals q;
+
+ 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
+test02a()
+{
+ struct quals
+ {
+ bool as_const;
+ bool as_lvalue;
+ };
+ struct F
+ {
+ quals operator()(int, int&) const { return { false, true }; }
+ quals operator()(int, int const&) const { return { true, true }; }
+ quals operator()(int, int&&) const { return { false, false }; }
+ quals operator()(int, int const&&) const { return { true, false }; }
+ };
+ constexpr F f{};
+
+ // verify propagation
+ auto h = bind_back<f>(10);
+ auto const& ch = h;
+ quals q;
+
+ q = h(0);
+ VERIFY( !q.as_const && q.as_lvalue );
+ q = ch(0);
+ VERIFY( q.as_const && q.as_lvalue );
+ q = std::move(h)(0);
+ VERIFY( !q.as_const && !q.as_lvalue );
+ q = std::move(ch)(0);
+ 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;
+
+ // Bound arg always forwarded as const int& so can only call second overload:
+
+ auto g0 = bind_back<f>(); // call wrapper has no bound arg
+ using G0 = decltype(g0);
+ static_assert(is_invocable_r_v<void*, G0&, void*, int>);
+ static_assert(is_invocable_r_v<void*, G0&, void*, int&>);
+ static_assert(is_invocable_r_v<void*, G0&, void*, const int&>);
+ static_assert(is_invocable_r_v<void*, const G0&, void*, int>);
+ static_assert(is_invocable_r_v<void*, G0&&, void*, int>);
+ void* p0 = static_cast<G0&&>(g0)(vp, i);
+ VERIFY( p0 == vp );
+
+ auto g1 = bind_back<f>(i); // call wrapper has bound arg of type int
+ using G1 = decltype(g1);
+ static_assert(!is_invocable_r_v<int&, G1&, void*>);
+ 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 );
+
+ auto g2 = bind_back<f>(std::ref(i)); // bound arg of type int&
+ using G2 = decltype(g2);
+ static_assert(is_invocable_r_v<void*, G2&, void*>);
+ static_assert(is_invocable_r_v<void*, G2&&, void*>);
+ static_assert(is_invocable_r_v<void*, const G2&, void*>);
+ static_assert(is_invocable_r_v<void*, const G2&&, void*>);
+ void* p2 = g2(vp);
+ VERIFY( p2 == vp );
+ p2 = static_cast<G2&&>(g2)(vp);
+ VERIFY( p2 == vp );
+ 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);
+ 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*>);
+}
+
+void test03a()
+{
+ struct F
+ {
+ int& operator()(void*, int& i) { return i; }
+ void* operator()(void* p, long) const { return p; }
+ };
+
+ int i = 5;
+ void* vp = &vp; // arbitrary void* value
+ constexpr F f;
+
+ // Bound arg always forwarded as const int& so can only call second overload:
+ auto g1 = bind_back<f>(i); // call wrapper has bound arg of type int
+ using G1 = decltype(g1);
+ static_assert(is_invocable_r_v<void*, G1&, void*>);
+ 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 );
+}
+
+
+constexpr int f(int i, int j, int k) { return i + 2*j + 3*k; }
+
+consteval bool
+test04()
+{
+ constexpr auto g = bind_back<f>();
+ VERIFY( std::is_empty_v<decltype(g)> );
+ VERIFY(g(1, 2, 3) == 1 + 2*2 + 3*3 );
+ constexpr auto g1 = bind_back<f>(1);
+ VERIFY(g1(2, 3) == 3*1 + 1*2 + 2*3 );
+ VERIFY(bind_back<g>(1)(2, 3) == 3*1 + 1*2 + 2*3 );
+ constexpr auto g2 = bind_back<f>(1, 2);
+ VERIFY(g2(3) == 2*1 + 3*2 + 1*3 );
+ VERIFY(bind_back<g1>(2)(3) == 3*1 + 2*2 + 1*3 );
+ constexpr auto g3 = bind_back<f>(1, 2, 3);
+ VERIFY(g3() == 1 + 2*2 + 3*3);
+ VERIFY(bind_back<g2>(3)() == 1*2 + 2*3 + 3*1 );
+ return true;
+}
+
+struct C { int i = 0; };
+struct D : C { D(){} D(D&&) { ++i; } };
+int f5(D const& d1, D const& d2, D const& d3)
+{ return d1.i + d2.i + d3.i; }
+
+void test05()
+{
+ // Must move arguments into capture object, not construct in place
+ // like normal arguments.
+ VERIFY( bind_back<f5>(D{}, D{})(D{}) == 2 );
+}
+
+int
+main()
+{
+ test01();
+ test02();
+ test02a();
+ test03();
+ test03a();
+ static_assert(test04());
+ test05();
+}
diff --git
a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp_neg.cc
b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp_neg.cc
new file mode 100644
index 00000000000..d64d49e83f4
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp_neg.cc
@@ -0,0 +1,38 @@
+// { dg-do compile { target c++26 } }
+
+#include <functional>
+
+void f() {};
+using fp = decltype(&f);
+constexpr fp nfp = nullptr;
+
+struct A { void mf() const {} };
+using mfp = decltype(&A::mf);
+constexpr mfp nnmfp = &A::mf;
+constexpr mfp nmfp = nullptr;
+
+struct B { B() = default; B(B const&) = delete; };
+void bf(B const&) {};
+
+struct C { C() = default; C(C&&) = delete; };
+void cf(C&&) {};
+
+int main()
+{
+ std::bind_back<f>()();
+ // Verify bind_back<fn> with fn a null pointer fails.
+ std::bind_back<nfp>()(); // { dg-error "here" }
+
+ std::bind_back<nnmfp>(A{})();
+ // Verify bind_back<mfn> with mfn a null member pointer fails.
+ std::bind_back<nmfp>(A{})(); // { dg-error "here" }
+
+ // Verify passing uncopyable type fails.
+ std::bind_back<bf>(B{}); // { dg-error "here" }
+ //
+ // Verify passing unmovable type fails.
+ std::bind_back<cf>(C{}); // { dg-error "here" }
+}
+
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
+// { dg-error "use of deleted function" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc
b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc
index 57482c52263..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..1287004460e
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp.cc
@@ -0,0 +1,260 @@
+// { 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()(int) {} };
+ constexpr F f{};
+
+ // 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, int) & { return { false, true }; }
+ quals operator()(int, int) const & { return { true, true }; }
+ quals operator()(int, int) && { return { false, false }; }
+ quals operator()(int, int) const && { return { true, false }; }
+ };
+
+ // Constness and value category forwarded to the target object?
+ { // no bound args
+ 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(0,0);
+ VERIFY( q.as_const && q.as_lvalue );
+ q = std::move(g)(0,0);
+ VERIFY( q.as_const && q.as_lvalue );
+ q = cg(0,0);
+ VERIFY( q.as_const && q.as_lvalue );
+ q = std::move(cg)(0,0);
+ VERIFY( q.as_const && q.as_lvalue );
+ }
+ { // one bound arg (for when we implement that as a separate case)
+ constexpr F f;
+ auto g = bind_front<f>(0);
+ const auto& cg = g;
+ quals q;
+
+ // Constness and value category forwarded to the target object?
+ 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 );
+ }
+ { // two bound args, the general case
+ constexpr F f;
+ auto g = bind_front<f>(0,0);
+ const auto& cg = g;
+ quals q;
+
+ 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
+test02a()
+{
+ struct quals
+ {
+ bool as_const;
+ bool as_lvalue;
+ };
+
+ struct F
+ {
+ quals operator()(int&, int) const { return { false, true }; }
+ quals operator()(int const&, int) const { return { true, true }; }
+ quals operator()(int&&, int) const { return { false, false }; }
+ quals operator()(int const&&, int) const { return { true, false }; }
+ };
+ constexpr F f{};
+
+ // verify propagation
+ auto h = bind_front<f>(10);
+ auto const& ch = h;
+ quals q;
+
+ q = h(0);
+ VERIFY( !q.as_const && q.as_lvalue );
+ q = ch(0);
+ VERIFY( q.as_const && q.as_lvalue );
+ q = std::move(h)(0);
+ VERIFY( !q.as_const && !q.as_lvalue );
+ q = std::move(ch)(0);
+ VERIFY( q.as_const && !q.as_lvalue );
+}
+
+void
+test03()
+{
+ struct F
+ {
+ int& operator()(int& i, void*) { return i; }
+ void* operator()(long, void* p) const { return p; }
+ };
+
+ int i = 5;
+ void* vp = &vp; // arbitrary void* value
+ constexpr F f;
+
+ // Bound arg always forwarded as const int& so can only call second overload:
+
+ auto g0 = bind_front<f>(); // call wrapper has no bound arg
+ using G0 = decltype(g0);
+ static_assert(is_invocable_r_v<void*, G0&, int, void*>);
+ static_assert(is_invocable_r_v<void*, G0&, int&, void*>);
+ static_assert(is_invocable_r_v<void*, G0&, const int&, void*>);
+ static_assert(is_invocable_r_v<void*, const G0&, int, void*>);
+ static_assert(is_invocable_r_v<void*, G0&&, int, void*>);
+ void* p0 = static_cast<G0&&>(g0)(i, vp);
+ VERIFY( p0 == vp );
+
+ auto g1 = bind_front<f>(i); // call wrapper has bound arg of type int
+ using G1 = decltype(g1);
+ static_assert(!is_invocable_r_v<int&, G1&, void*>);
+ 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 );
+
+ auto g2 = bind_front<f>(std::ref(i)); // bound arg of type int&
+ using G2 = decltype(g2);
+ static_assert(is_invocable_r_v<void*, G2&, void*>);
+ static_assert(is_invocable_r_v<void*, G2&&, void*>);
+ static_assert(is_invocable_r_v<void*, const G2&, void*>);
+ static_assert(is_invocable_r_v<void*, const G2&&, void*>);
+ void* p2 = g2(vp);
+ VERIFY( p2 == vp );
+ p2 = static_cast<G2&&>(g2)(vp);
+ VERIFY( p2 == vp );
+ 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);
+ 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*>);
+}
+
+void test03a()
+{
+ struct F
+ {
+ int& operator()(int& i, void*) { return i; }
+ void* operator()(long, void* p) const { return p; }
+ };
+
+ int i = 5;
+ void* vp = &vp; // arbitrary void* value
+ constexpr F f;
+
+ // Bound arg always forwarded as const int& so can only call second overload:
+ auto g1 = bind_front<f>(i); // call wrapper has bound arg of type int
+ using G1 = decltype(g1);
+ static_assert(is_invocable_r_v<void*, G1&, void*>);
+ 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 );
+}
+
+constexpr int f(int i, int j, int k) { return i + 2*j + 3*k; }
+
+consteval bool
+test04()
+{
+ constexpr auto g = bind_front<f>();
+ VERIFY( std::is_empty_v<decltype(g)> );
+ VERIFY( g(1, 2, 3) == 1 + 2*2 + 3*3 );
+ constexpr auto g1 = bind_front<f>(1);
+ VERIFY( g1(2, 3) == 1 + 2*2 + 3*3 );
+ VERIFY( bind_front<g>(1)(2, 3) == 1 + 2*2 + 3*3 );
+ constexpr auto g2 = bind_front<f>(1, 2);
+ VERIFY( g2(3) == 1 + 2*2 + 3*3 );
+ VERIFY( bind_front<g1>(2)(3) == 1 + 2*2 + 3*3 );
+ constexpr auto g3 = bind_front<f>(1, 2, 3);
+ VERIFY( g3() == 1 + 2*2 + 3*3 );
+ VERIFY(bind_front<g2>(3)() == 1 + 2*2 + 3*3 );
+ return true;
+}
+
+struct C { int i = 0; };
+struct D : C { D(){} D(D&&) { ++i; } };
+int f5(D const& d1, D const& d2, D const& d3)
+{ return d1.i + d2.i + d3.i; }
+
+void test05()
+{
+ // Must move arguments into capture object, not construct in place
+ // like normal arguments.
+ VERIFY( bind_front<f5>(D{}, D{})(D{}) == 2 );
+}
+
+int
+main()
+{
+ test01();
+ test02();
+ test02a();
+ test03();
+ test03a();
+ static_assert(test04());
+ test05();
+}
diff --git
a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp_neg.cc
b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp_neg.cc
new file mode 100644
index 00000000000..d274c191635
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp_neg.cc
@@ -0,0 +1,38 @@
+// { dg-do compile { target c++26 } }
+
+#include <functional>
+
+void f() {}
+using fp = decltype(&f);
+constexpr fp nfp = nullptr;
+
+struct A { void mf() const {} };
+using mfp = decltype(&A::mf);
+constexpr mfp nnmfp = &A::mf;
+constexpr mfp nmfp = nullptr;
+
+struct B { B() = default; B(B const&) = delete; };
+void bf(B const&) {};
+
+struct C { C() = default; C(C&&) = delete; };
+void cf(C&&) {};
+
+int main()
+{
+ std::bind_front<f>()();
+ // Verify bind_front<fn> with fn a null pointer fails:
+ std::bind_front<nfp>()(); // { dg-error "here" }
+
+ std::bind_front<nnmfp>(A{})();
+ // Verify bind_front<mfn> with mfn a null member pointer fails:
+ std::bind_front<nmfp>(A{})(); // { dg-error "here" }
+
+ // Verify passing uncopyable type fails:
+ std::bind_front<bf>(B{}); // { dg-error "here" }
+ //
+ // Verify passing unmovable type fails:
+ std::bind_front<cf>(C{}); // { dg-error "here" }
+}
+
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
+// { dg-error "use of deleted function" "" { target *-*-* } 0 }
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..f0b07e7acd8
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/nttp.cc
@@ -0,0 +1,98 @@
+// 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