[libcxx-commits] [libcxx] a3f244e - [libc++][functional] Implement `std::bind_front<NTTP>` (#165096)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Apr 19 08:59:32 PDT 2026
Author: Jakub Mazurkiewicz
Date: 2026-04-19T23:59:28+08:00
New Revision: a3f244e2d5550e110daee562b88bee247b75d66c
URL: https://github.com/llvm/llvm-project/commit/a3f244e2d5550e110daee562b88bee247b75d66c
DIFF: https://github.com/llvm/llvm-project/commit/a3f244e2d5550e110daee562b88bee247b75d66c.diff
LOG: [libc++][functional] Implement `std::bind_front<NTTP>` (#165096)
Add `std::bind_front<NTTP>` function from
[P2714R1](https://wg21.link/p2714r1).
Towards https://github.com/llvm/llvm-project/issues/105388.
Added:
libcxx/test/libcxx/utilities/function.objects/func.bind.partial/bind_front.nttp.compile.pass.cpp
libcxx/test/libcxx/utilities/function.objects/func.bind.partial/bind_front.nttp.nodiscard.verify.cpp
libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.nttp.pass.cpp
libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.nttp.verify.cpp
Modified:
libcxx/include/__functional/bind_front.h
libcxx/include/functional
libcxx/test/std/utilities/function.objects/func.bind.partial/types.h
Removed:
################################################################################
diff --git a/libcxx/include/__functional/bind_front.h b/libcxx/include/__functional/bind_front.h
index 427accf96339d..97f8f4c458702 100644
--- a/libcxx/include/__functional/bind_front.h
+++ b/libcxx/include/__functional/bind_front.h
@@ -17,6 +17,8 @@
#include <__type_traits/decay.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_member_pointer.h>
+#include <__type_traits/is_pointer.h>
#include <__utility/forward.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -49,6 +51,53 @@ template <class _Fn, class... _Args>
#endif // _LIBCPP_STD_VER >= 20
+#if _LIBCPP_STD_VER >= 26
+
+template <auto _Fn, class _Indices, class... _BoundArgs>
+struct __nttp_bind_front_t;
+
+template <auto _Fn, size_t... _Indices, class... _BoundArgs>
+struct __nttp_bind_front_t<_Fn, index_sequence<_Indices...>, _BoundArgs...> {
+ tuple<_BoundArgs...> __bound_args_;
+
+ template <class _Self, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(this _Self&& __self, _Args&&... __args) noexcept(noexcept(std::invoke(
+ _Fn, std::get<_Indices>(std::forward<_Self>(__self).__bound_args_)..., std::forward<_Args>(__args)...)))
+ -> decltype(std::invoke(
+ _Fn, std::get<_Indices>(std::forward<_Self>(__self).__bound_args_)..., std::forward<_Args>(__args)...)) {
+ return std::invoke(
+ _Fn, std::get<_Indices>(std::forward<_Self>(__self).__bound_args_)..., std::forward<_Args>(__args)...);
+ }
+};
+
+template <auto _Fn>
+struct __nttp_bind_without_bound_args_t {
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI static constexpr auto
+ operator()(_Args&&... __args) noexcept(noexcept(std::invoke(_Fn, std::forward<_Args>(__args)...)))
+ -> decltype(std::invoke(_Fn, std::forward<_Args>(__args)...)) {
+ return std::invoke(_Fn, std::forward<_Args>(__args)...);
+ }
+};
+
+template <auto _Fn, class... _Args>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto bind_front(_Args&&... __args) {
+ static_assert((is_constructible_v<decay_t<_Args>, _Args> && ...),
+ "bind_front requires all decay_t<Args> to be constructible from respective Args");
+ static_assert((is_move_constructible_v<decay_t<_Args>> && ...),
+ "bind_front requires all decay_t<Args> to be move constructible");
+ if constexpr (using _Ty = decltype(_Fn); is_pointer_v<_Ty> || is_member_pointer_v<_Ty>)
+ static_assert(_Fn != nullptr, "bind_front: f cannot be equal to nullptr");
+
+ if constexpr (sizeof...(_Args) == 0)
+ return __nttp_bind_without_bound_args_t<_Fn>{};
+ else
+ return __nttp_bind_front_t<_Fn, index_sequence_for<_Args...>, decay_t<_Args>...>{
+ .__bound_args_{std::forward<_Args>(__args)...}};
+}
+
+#endif // _LIBCPP_STD_VER >= 26
+
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___FUNCTIONAL_BIND_FRONT_H
diff --git a/libcxx/include/functional b/libcxx/include/functional
index 9ebcd818ec840..020754a96432c 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -221,6 +221,8 @@ template <auto f>
// [func.bind.partial], function templates bind_front and bind_back
template<class F, class... Args>
constexpr unspecified bind_front(F&&, Args&&...); // C++20
+template<auto f, class... Args>
+ constexpr unspecified bind_front(Args&&...); // C++26
template<class F, class... Args>
constexpr unspecified bind_back(F&&, Args&&...); // C++23
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.bind.partial/bind_front.nttp.compile.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.bind.partial/bind_front.nttp.compile.pass.cpp
new file mode 100644
index 0000000000000..f46cc7908862e
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.bind.partial/bind_front.nttp.compile.pass.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <functional>
+
+// Type of `std::bind_front<NTTP>(/* no bound args */)` is empty.
+
+#include <functional>
+#include <type_traits>
+
+struct NonEmptyFunctionObject {
+ int val = true;
+ void operator()() const;
+};
+
+void func();
+
+struct SomeClass {
+ long member_object;
+ void member_function();
+};
+
+using ResultWithEmptyFuncObject = decltype(std::bind_front<std::integral_constant<int, 0>{}>());
+static_assert(std::is_empty_v<ResultWithEmptyFuncObject>);
+
+using ResultWithNotEmptyFuncObject = decltype(std::bind_front<NonEmptyFunctionObject{}>());
+static_assert(std::is_empty_v<ResultWithNotEmptyFuncObject>);
+
+using ResultWithFunctionPointer = decltype(std::bind_front<func>());
+static_assert(std::is_empty_v<ResultWithFunctionPointer>);
+
+using ResultWithMemberObjectPointer = decltype(std::bind_front<&SomeClass::member_object>());
+static_assert(std::is_empty_v<ResultWithMemberObjectPointer>);
+
+using ResultWithMemberFunctionPointer = decltype(std::bind_front<&SomeClass::member_function>());
+static_assert(std::is_empty_v<ResultWithMemberFunctionPointer>);
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.bind.partial/bind_front.nttp.nodiscard.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.bind.partial/bind_front.nttp.nodiscard.verify.cpp
new file mode 100644
index 0000000000000..3fcee5b9a2bad
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.bind.partial/bind_front.nttp.nodiscard.verify.cpp
@@ -0,0 +1,19 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <functional>
+
+// Test the libc++ extension that std::bind_front<NTTP> is marked as [[nodiscard]].
+
+#include <functional>
+
+void test() {
+ std::bind_front<test>(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.nttp.pass.cpp b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.nttp.pass.cpp
new file mode 100644
index 0000000000000..efebb6026091f
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.nttp.pass.cpp
@@ -0,0 +1,388 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <functional>
+
+// template<auto f, class... Args>
+// constexpr unspecified bind_front(Args&&...);
+
+#include <functional>
+
+#include <cassert>
+#include <concepts>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.h"
+#include "types.h"
+
+constexpr void test_basic_bindings() {
+ { // Bind arguments, call without arguments
+ {
+ auto f = std::bind_front<MakeTuple{}>();
+ assert(f() == std::make_tuple());
+ }
+ {
+ auto f = std::bind_front<MakeTuple{}>(Elem<1>{});
+ assert(f() == std::make_tuple(Elem<1>{}));
+ }
+ {
+ auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{});
+ assert(f() == std::make_tuple(Elem<1>{}, Elem<2>{}));
+ }
+ {
+ auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{}, Elem<3>{});
+ assert(f() == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}));
+ }
+ }
+
+ { // Bind no arguments, call with arguments
+ {
+ auto f = std::bind_front<MakeTuple{}>();
+ assert(f(Elem<1>{}) == std::make_tuple(Elem<1>{}));
+ }
+ {
+ auto f = std::bind_front<MakeTuple{}>();
+ assert(f(Elem<1>{}, Elem<2>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}));
+ }
+ {
+ auto f = std::bind_front<MakeTuple{}>();
+ assert(f(Elem<1>{}, Elem<2>{}, Elem<3>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}));
+ }
+ }
+
+ { // Bind arguments, call with arguments
+ {
+ auto f = std::bind_front<MakeTuple{}>(Elem<1>{});
+ assert(f(Elem<10>{}) == std::make_tuple(Elem<1>{}, Elem<10>{}));
+ }
+ {
+ auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{});
+ assert(f(Elem<10>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<10>{}));
+ }
+ {
+ auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{}, Elem<3>{});
+ assert(f(Elem<10>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}, Elem<10>{}));
+ }
+
+ {
+ auto f = std::bind_front<MakeTuple{}>(Elem<1>{});
+ assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<1>{}, Elem<10>{}, Elem<11>{}));
+ }
+ {
+ auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{});
+ assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<10>{}, Elem<11>{}));
+ }
+ {
+ auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{}, Elem<3>{});
+ assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}, Elem<10>{}, Elem<11>{}));
+ }
+ {
+ auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{}, Elem<3>{});
+ assert(f(Elem<10>{}, Elem<11>{}, Elem<12>{}) ==
+ std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}, Elem<10>{}, Elem<11>{}, Elem<12>{}));
+ }
+ }
+
+ { // Basic tests with fundamental types
+ const int n = 2;
+ const int m = 1;
+ int o = 0;
+
+ auto add = [](int x, int y) { return x + y; };
+ auto add6 = [](int a, int b, int c, int d, int e, int f) { return a + b + c + d + e + f; };
+ auto increment = [](int& x) { return ++x; };
+
+ auto a = std::bind_front<add>(m, n);
+ assert(a() == 3);
+
+ auto b = std::bind_front<add6>(m, n, m, m, m, m);
+ assert(b() == 7);
+
+ auto c = std::bind_front<add6>(n, m);
+ assert(c(1, 1, 1, 1) == 7);
+
+ auto f = std::bind_front<add>(n);
+ assert(f(3) == 5);
+
+ auto g = std::bind_front<add>(n, 1);
+ assert(g() == 3);
+
+ auto h = std::bind_front<add6>(1, 1, 1);
+ assert(h(2, 2, 2) == 9);
+
+ auto i = std::bind_front<increment>();
+ assert(i(o) == 1);
+ assert(o == 1);
+
+ auto j = std::bind_front<increment>(std::ref(o));
+ assert(j() == 2);
+ assert(o == 2);
+ }
+}
+
+constexpr void test_edge_cases() {
+ { // Make sure we don't treat std::reference_wrapper specially.
+ auto sub = [](std::reference_wrapper<int> a, std::reference_wrapper<int> b) { return a.get() - b.get(); };
+
+ int i = 1;
+ int j = 2;
+ auto f = std::bind_front<sub>(std::ref(i));
+ assert(f(std::ref(j)) == -1);
+ }
+
+ { // Make sure we can call a function that's a pointer to a member function.
+ struct MemberFunction {
+ constexpr int mul(int x, int y) { return x * y; }
+ };
+
+ MemberFunction value;
+ auto fn = std::bind_front<&MemberFunction::mul>(value, 2);
+ assert(fn(3) == 6);
+ }
+
+ { // Make sure we can call a function that's a pointer to a member object.
+ struct MemberObject {
+ int obj;
+ };
+
+ MemberObject value{.obj = 3};
+ auto fn1 = std::bind_front<&MemberObject::obj>();
+ assert(fn1(value) == 3);
+ auto fn2 = std::bind_front<&MemberObject::obj>(value);
+ assert(fn2() == 3);
+ }
+}
+
+constexpr void test_passing_arguments() {
+ { // Make sure that we copy the bound arguments into the unspecified-type.
+ int n = 2;
+ auto f = std::bind_front<[](int x, int y) { return x + y; }>(n, 1);
+ n = 100;
+ assert(f() == 3);
+ }
+
+ { // Make sure we pass the bound arguments to the function object
+ // with the right value category.
+ {
+ auto was_copied = [](CopyMoveInfo info) { return info.copy_kind == CopyMoveInfo::copy; };
+ CopyMoveInfo info;
+ auto f = std::bind_front<was_copied>(info);
+ assert(f());
+ }
+
+ {
+ auto was_moved = [](CopyMoveInfo info) { return info.copy_kind == CopyMoveInfo::move; };
+ CopyMoveInfo info;
+ auto f = std::bind_front<was_moved>(info);
+ assert(std::move(f)());
+ }
+ }
+}
+
+constexpr void test_perfect_forwarding_call_wrapper() {
+ { // Make sure we call the correctly cv-ref qualified operator()
+ // based on the value category of the bind_front<NTTP> unspecified-type.
+ struct X {
+ constexpr int operator()() & { return 1; }
+ constexpr int operator()() const& { return 2; }
+ constexpr int operator()() && { return 3; }
+ constexpr int operator()() const&& { return 4; }
+ };
+
+ auto f = std::bind_front<X{}>();
+ using F = decltype(f);
+ assert(static_cast<F&>(f)() == 2);
+ assert(static_cast<const F&>(f)() == 2);
+ assert(static_cast<F&&>(f)() == 2);
+ assert(static_cast<const F&&>(f)() == 2);
+ }
+
+ // Call to `bind_front<NTTP>` unspecified-type's operator() should always result in call to the const& overload of the underlying function object.
+ {
+ { // Make sure unspecified-type is still callable when we delete the & overload.
+ struct X {
+ int operator()() & = delete;
+ int operator()() const&;
+ int operator()() &&;
+ int operator()() const&&;
+ };
+
+ using F = decltype(std::bind_front<X{}>());
+ static_assert(std::invocable<F&>);
+ static_assert(std::invocable<const F&>);
+ static_assert(std::invocable<F>);
+ static_assert(std::invocable<const F>);
+ }
+
+ { // Make sure unspecified-type is not callable when we delete the const& overload.
+ struct X {
+ int operator()() &;
+ int operator()() const& = delete;
+ int operator()() &&;
+ int operator()() const&&;
+ };
+
+ using F = decltype(std::bind_front<X{}>());
+ static_assert(!std::invocable<F&>);
+ static_assert(!std::invocable<const F&>);
+ static_assert(!std::invocable<F>);
+ static_assert(!std::invocable<const F>);
+ }
+
+ { // Make sure unspecified-type is still callable when we delete the && overload.
+ struct X {
+ int operator()() &;
+ int operator()() const&;
+ int operator()() && = delete;
+ int operator()() const&&;
+ };
+
+ using F = decltype(std::bind_front<X{}>());
+ static_assert(std::invocable<F&>);
+ static_assert(std::invocable<const F&>);
+ static_assert(std::invocable<F>);
+ static_assert(std::invocable<const F>);
+ }
+
+ { // Make sure unspecified-type is still callable when we delete the const&& overload.
+ struct X {
+ int operator()() &;
+ int operator()() const&;
+ int operator()() &&;
+ int operator()() const&& = delete;
+ };
+
+ using F = decltype(std::bind_front<X{}>());
+ static_assert(std::invocable<F&>);
+ static_assert(std::invocable<const F&>);
+ static_assert(std::invocable<F>);
+ static_assert(std::invocable<const F>);
+ }
+ }
+
+ { // Test perfect forwarding when various overloads are available
+ struct X {
+ constexpr Tag<0> operator()(int&, char) const { return {}; }
+ constexpr Tag<1> operator()(const int&, char) const { return {}; }
+ constexpr Tag<2> operator()(int&&, char) const { return {}; }
+ constexpr Tag<3> operator()(const int&&, char) const { return {}; }
+ };
+
+ auto f = std::bind_front<X{}>(0);
+ std::same_as<Tag<0>> auto _ = f('a');
+ std::same_as<Tag<1>> auto _ = std::as_const(f)('a');
+ std::same_as<Tag<2>> auto _ = std::move(f)('a');
+ std::same_as<Tag<3>> auto _ = std::move(std::as_const(f))('a');
+ }
+
+ { // Test perfect forwarding
+ auto f = [](int& val) {
+ val = 5;
+ return 10;
+ };
+
+ auto bf = std::bind_front<f>();
+ int val = 0;
+ assert(bf(val) == 10);
+ assert(val == 5);
+
+ using BF = decltype(bf);
+ static_assert(std::invocable<BF, int&>);
+ static_assert(!std::invocable<BF, int>);
+ }
+}
+
+constexpr void test_return_type() {
+ { // Test constructors and assignment operators
+ struct LeftShift {
+ constexpr unsigned int operator()(unsigned int x, unsigned int y) const { return x << y; }
+ };
+
+ auto power_of_2 = std::bind_front<LeftShift{}>(1);
+ assert(power_of_2(5) == 32U);
+ assert(power_of_2(4) == 16U);
+
+ auto moved = std::move(power_of_2);
+ assert(moved(6) == 64);
+ assert(moved(7) == 128);
+
+ auto copied = power_of_2;
+ assert(copied(3) == 8);
+ assert(copied(2) == 4);
+
+ moved = std::move(copied);
+ assert(copied(1) == 2);
+ assert(copied(0) == 1);
+
+ copied = moved;
+ assert(copied(8) == 256);
+ assert(copied(9) == 512);
+ }
+
+ { // Make sure `bind_front<NTTP>` unspecified-type's operator() is SFINAE-friendly.
+ using F = decltype(std::bind_front<[](int x, int y) { return x / y; }>(1));
+ static_assert(!std::is_invocable<F>::value);
+ static_assert(std::is_invocable<F, int>::value);
+ static_assert(!std::is_invocable<F, void*>::value);
+ static_assert(!std::is_invocable<F, int, int>::value);
+ }
+
+ { // Test noexceptness
+ auto always_noexcept = std::bind_front<MaybeNoexceptFn<true>{}>();
+ static_assert(noexcept(always_noexcept()));
+
+ auto never_noexcept = std::bind_front<MaybeNoexceptFn<false>{}>();
+ static_assert(!noexcept(never_noexcept()));
+ }
+
+#ifndef TEST_HAS_NO_EXCEPTIONS
+# ifndef __cpp_constexpr_exceptions
+ if !consteval
+# endif
+ { // Test exception propagation
+ auto throws = [](int x, int y) { throw x + y; };
+ auto f = std::bind_front<throws>(10);
+ static_assert(!noexcept(f(20)));
+
+ try {
+ f(20);
+ } catch (int z) {
+ assert(z == 30);
+ }
+ }
+#endif // TEST_HAS_NO_EXCEPTIONS
+
+ { // Test calling volatile wrapper -- we allow it as an extension
+ using Fn = decltype(std::bind_front<std::integral_constant<int, 0>{}>());
+ static_assert(std::invocable<volatile Fn&>);
+ static_assert(std::invocable<const volatile Fn&>);
+ static_assert(std::invocable<volatile Fn>);
+ static_assert(std::invocable<const volatile Fn>);
+ }
+}
+
+constexpr bool test() {
+ test_basic_bindings();
+ test_edge_cases();
+ test_passing_arguments();
+ test_perfect_forwarding_call_wrapper();
+ test_return_type();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.nttp.verify.cpp b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.nttp.verify.cpp
new file mode 100644
index 0000000000000..d7ed90f87562b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/bind_front.nttp.verify.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <functional>
+
+// template<auto f, class... Args>
+// constexpr unspecified bind_front(Args&&...);
+// Mandates:
+// - (is_constructible_v<BoundArgs, Args> && ...) is true, and
+// - (is_move_constructible_v<BoundArgs> && ...) is true, and
+// - If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
+
+#include <functional>
+
+struct AnyArgs {
+ template <class... Args>
+ void operator()(Args&&...) {}
+};
+
+void test() {
+ { // (is_constructible_v<BoundArgs, Args> && ...) is true
+ struct Arg {
+ Arg() = default;
+ Arg(const Arg&) = default;
+ Arg(Arg&) = delete;
+ };
+
+ Arg arg;
+ auto _ = std::bind_front<AnyArgs{}>(arg);
+ // expected-error@*:* {{static assertion failed due to requirement 'is_constructible_v<Arg, Arg &>': bind_front requires all decay_t<Args> to be constructible from respective Args}}
+ // expected-error@*:* 0-1{{call to deleted constructor of 'Arg'}}
+ }
+
+ { // (is_move_constructible_v<BoundArgs> && ...) is true
+ struct Arg {
+ Arg() = default;
+ Arg(Arg&&) = delete;
+ Arg(const Arg&) = default;
+ };
+
+ Arg arg;
+ auto _ = std::bind_front<AnyArgs{}>(arg);
+ // expected-error@*:* {{static assertion failed due to requirement 'is_move_constructible_v<Arg>': bind_front requires all decay_t<Args> to be move constructible}}
+ }
+
+ { // If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true
+ struct X {};
+
+ auto _ = std::bind_front<static_cast<void (*)()>(nullptr)>();
+ // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': bind_front: f cannot be equal to nullptr}}
+
+ auto _ = std::bind_front<static_cast<int X::*>(nullptr)>();
+ // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': bind_front: f cannot be equal to nullptr}}
+
+ auto _ = std::bind_front<static_cast<void (X::*)()>(nullptr)>();
+ // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': bind_front: f cannot be equal to nullptr}}
+ }
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.bind.partial/types.h b/libcxx/test/std/utilities/function.objects/func.bind.partial/types.h
index 76ed4d478baac..44e13c2931119 100644
--- a/libcxx/test/std/utilities/function.objects/func.bind.partial/types.h
+++ b/libcxx/test/std/utilities/function.objects/func.bind.partial/types.h
@@ -40,4 +40,12 @@ T do_nothing(T t) {
return t;
}
+template <bool IsNoexcept>
+struct MaybeNoexceptFn {
+ bool operator()() const noexcept(IsNoexcept); // not defined
+};
+
+template <int>
+struct Tag {};
+
#endif // TEST_STD_UTILITIES_FUNCTION_OBJECTS_FUNC_BIND_PARTIAL_TYPES_H
More information about the libcxx-commits
mailing list