On Tue, Jul 8, 2025 at 5:41 AM Nathan Myers <n...@cantrip.org> wrote:
> This is a snapshot of work in progress, for reference. > bind_front<f>(...) is uglified directly from the sample > implementation in P2714, at include/std/functional:1284 . > > Test failures: > > bind_front/1.cc:53: error: static assertion failed > bind_front/1.cc:57: error: static assertion failed > bind_front/1.cc:214: error: static assertion failed > bind_front/1.cc:215: error: static assertion failed > bind_front/1.cc:216: required from here > functional:1301: error: invalid conversion from > 'std::invoke_result_t<const test03()::F&, std::reference_wrapper<int>&, > void*&>' {aka 'void*'} to 'int' [-fpermissive] > [... etc. ] > Also complains about 218, 220, 231, 233-6, 264, 267 > The issue is raised on the line: int& i6 = g6(vp); VERIFY( &i6 == &i ); Where G6 is defined as follows: auto g6 = bind_front<f>(std::ref(i)); // bound arg of type int& using G6 = decltype(g6); And f: struct F { int& operator()(int& i, void*) { return i; } void* operator()(int, void* p) const { return p; } }; constexpr static F f{}; As the template parameter object, i.e. what id-expression f refers to in bind_front<f> is always constant, g6(vp) i.e. bind_front<f>(ref(i), vp) calls f(ref(i), vp), and because f is const qualified, the only viable candiate is: void* operator()(int, void* p) const { return p; } So you get void* returned, that int& obviously cannot bind to. > > 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_front/1.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_front/1.cc | 103 ++++++++++++++- > 4 files changed, 278 insertions(+), 5 deletions(-) > > 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_front/1.cc > b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc > index 57482c52263..d21432f9629 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 > @@ -47,6 +47,18 @@ test01() > decltype(bind_front(std::declval<F>(), std::declval<int>())), > decltype(bind_front(std::declval<const F&>(), std::declval<const > int&>())) > >); > +#if __cpp_lib_bind_front >= 202306 > + const F f{}; > + > + 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&>())) > + >); > +#endif > > // Reference wrappers should be handled: > static_assert(!std::is_same_v< > @@ -61,6 +73,20 @@ test01() > decltype(bind_front(std::declval<F>(), > std::ref(std::declval<int&>()))), > decltype(bind_front(std::declval<F>(), > std::cref(std::declval<int&>()))) > >); > +#if __cpp_lib_bind_front >= 202306 > + 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&>()))) > + >); > +#endif > } > > void > @@ -81,6 +107,7 @@ test02() > }; > > F f; > + > auto g = bind_front(f); > const auto& cg = g; > quals q; > @@ -94,6 +121,23 @@ test02() > VERIFY( q.as_const && q.as_lvalue ); > q = std::move(cg)(); > VERIFY( q.as_const && ! q.as_lvalue ); > + > +#if __cpp_lib_bind_front >= 202306 > + const F f2; > + auto g2 = bind_front<f2>(); > + const auto& cg2 = g2; > + quals q2; > + > + // constness and value category should be forwarded to the target > object: > + q2 = g2(); > + VERIFY( ! q2.as_const && q2.as_lvalue ); > + q2 = std::move(g)(); > + VERIFY( ! q2.as_const && ! q2.as_lvalue ); > + q2 = cg2(); > + VERIFY( q2.as_const && q2.as_lvalue ); > + q2 = std::move(cg2)(); > + VERIFY( q2.as_const && ! q2.as_lvalue ); > +#endif > } > > void > @@ -147,9 +191,53 @@ test03() > 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&&>); > + > +#if __cpp_lib_bind_front >= 202306 > + constexpr static F f{}; > + > + auto g5 = bind_front<f>(i); // call wrapper has bound arg of type int > + using G5 = decltype(g5); > + // Invoking G5& will pass g5's bound arg as int&, so calls first > overload: > + static_assert(is_invocable_r_v<int&, G5&, void*>); > + // Invoking const G5& or G&& calls second overload: > + static_assert(is_invocable_r_v<void*, const G5&, void*>); > + static_assert(is_invocable_r_v<void*, G5&&, void*>); > + void* p5 = static_cast<G5&&>(g5)(vp); > + VERIFY( p5 == vp ); > + > + auto g6 = bind_front<f>(std::ref(i)); // bound arg of type int& > + using G6 = decltype(g6); > + // Bound arg always forwarded as int& even from G6&& or const G6& > + static_assert(is_invocable_r_v<int&, G6&, void*>); > + static_assert(is_invocable_r_v<int&, G6&&, void*>); > + // But cannot call first overload on const G6: > + static_assert(is_invocable_r_v<void*, const G6&, void*>); > + static_assert(is_invocable_r_v<void*, const G6&&, void*>); > + int& i6 = g6(vp); > + VERIFY( &i6 == &i ); > + int& i6r = static_cast<G6&&>(g6)(vp); > + VERIFY( &i6r == &i ); > + void* p6 = const_cast<const G6&>(g6)(vp); > + VERIFY( p6 == vp ); > + > + auto g7 = bind_front<f>(std::cref(i)); // bound arg of type const int& > + using G7 = decltype(g7); > + // Bound arg always forwarded as const int& so can only call second > overload: > + static_assert(is_invocable_r_v<void*, G7&, void*>); > + static_assert(is_invocable_r_v<void*, G7&&, void*>); > + static_assert(is_invocable_r_v<void*, const G7&, void*>); > + static_assert(is_invocable_r_v<void*, const G7&&, void*>); > + > + auto g8 = bind_front<g7>(nullptr); > + using G8 = decltype(g8); > + static_assert(is_invocable_r_v<int&, G8&>); > + static_assert(is_invocable_r_v<int&, G8&&>); > + static_assert(is_invocable_r_v<void*, const G8&>); > + static_assert(is_invocable_r_v<void*, const G8&&>); > +#endif > } > > -int f(int i, int j, int k) { return i + j + k; } > +constexpr int f(int i, int j, int k) { return i + j + k; } > > void > test04() > @@ -165,6 +253,19 @@ test04() > auto g3 = bind_front(f, 1, 2, 3); > VERIFY( g3() == 6 ); > VERIFY( bind_front(g2, 3)() == 6 ); > +#if __cpp_lib_bind_front >= 202306 > + const auto g4 = bind_front<f>(); > + VERIFY( g4(1, 2, 3) == 6 ); > + const auto g5 = bind_front<f>(1); > + VERIFY( g5(2, 3) == 6 ); > + VERIFY( bind_front<g4>(1)(2, 3) == 6 ); > + const auto g6 = bind_front<f>(1, 2); > + VERIFY( g6(3) == 6 ); > + VERIFY( bind_front<g5>(2)(3) == 6 ); > + const auto g7 = bind_front<f>(1, 2, 3); > + VERIFY( g7() == 6 ); > + VERIFY( bind_front<g6>(3)() == 6 ); > +#endif > } > > int > -- > 2.50.0 > >