[SNAPv4] libstdc++: Add NTTP bind_front, -back, not_fn (P2714) [PR119744]
Nathan Myers
ncm@cantrip.org
Tue Jul 8 03:40:17 GMT 2025
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
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
More information about the Libstdc++
mailing list