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