I have left some comments on the previous revision here:
https://gcc.gnu.org/pipermail/libstdc++/2025-July/062349.html
They do not seem to be addressed.

On Thu, Jul 17, 2025 at 9:13 PM Nathan Myers <n...@cantrip.org> wrote:

> [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
>
>

Reply via email to