On Mon, 15 Sep 2025 at 09:39 -0400, Nathan Myers wrote:
Changes in v10:
* Merge external updates
* Use long literals "202306L" in #if guards
* Remove redundant #include <type_traits>
* Test constexpr not_fn
* Fix return type declarations in synopsis.cc test
* Fix error text for __cpp_lib_not_fn check in test
* Use __and_v<> in preference to __and<>::value where version permits

[snip]

@@ -922,6 +922,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                                          std::forward<_BoundArgs>(__args)...);
    }

+#if __cpp_lib_bind_front >= 202306L || __cpp_lib_bind_back >= 202306L
+  template <auto __fn>
+    struct _Bind_fn_t
+    {
+      using _Fn = const decltype(__fn)&;
+      template <typename... _Args>
+       constexpr static decltype(auto)
+       operator()(_Args... __args)
+         noexcept(is_nothrow_invocable_v<_Fn, _Args...>)
+         requires is_invocable_v<_Fn, _Args...>
+       { return std::invoke(__fn, std::forward<_Args>(__args)...); }
+    };
+#endif
+
#ifdef __cpp_lib_bind_front // C++ >= 20
  /** Create call wrapper by partial application of arguments to function.
   *
@@ -941,6 +955,50 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
      return _Bind_front_t<_Fn, _Args...>(0, std::forward<_Fn>(__fn),
                                          std::forward<_Args>(__args)...);
    }
+
+#if __cpp_lib_bind_front >= 202306L
+
+  /** Create call wrapper by partial application of arguments to function.
+   *
+   * The result of `std::bind_front<fn>(bind_args...)` is a function object
+   * that stores the bound arguments, `bind_args...`. When that function
+   * object is invoked with `call_args...` it returns the result of calling
+   * `fn(bind_args..., call_args...)`.
+   *
+   *  @since C++26
+   */
+  template<auto __fn, typename... _BindArgs>
+    constexpr decltype(auto)
+    bind_front(_BindArgs&&... __bind_args)
+      noexcept(__and_v<is_nothrow_constructible<_BindArgs>...>)
+    {
+      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
@@ -962,6 +1020,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 >= 202306L
+
+  /** 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_v<is_nothrow_constructible<_BindArgs>...>)
+    {
+      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

[snip]

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

This constant has an extra 0 in it, and there should be an 'L' suffix
on the three 202306 constants in this file too.

+  template <auto f> constexpr decltype(auto) not_fn() noexcept;
+#endif
+#endif

  //  lib.binders, binders:
  template <class Operation>  class binder1st;
@@ -65,6 +72,22 @@ 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 decltype(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 decltype(auto) bind_back(Args&&...);
+#endif
+#endif

The bind_front<f> and bind_back<f> declarations here have no exception
spec, which matches the declarations in the standard. But on the
definitions in <functional> you've strengthened them to have a
conditional noexcept-specifier. Doesn't that cause the test to fail if
you run it with -std=c++26, e.g.

GLIBCXX_TESTSUITE_STDS=26 make check 
RUNTESTFLAGS=conformance.exp=20_util/headers/functional/synopsis.cc


Reply via email to