diff --git a/libcxx/include/__functional/bind_front.h b/libcxx/include/__functional/bind_front.h --- a/libcxx/include/__functional/bind_front.h +++ b/libcxx/include/__functional/bind_front.h @@ -24,25 +24,31 @@ #if _LIBCPP_STD_VER > 17 -struct __bind_front_op -{ - template - constexpr static auto __call(_Args&&... __args) - noexcept(noexcept(_VSTD::invoke(_VSTD::forward<_Args>(__args)...))) - -> decltype( _VSTD::invoke(_VSTD::forward<_Args>(__args)...)) - { return _VSTD::invoke(_VSTD::forward<_Args>(__args)...); } +struct __bind_front_op { + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Args&& ...__args) const + noexcept(noexcept(_VSTD::invoke(_VSTD::forward<_Args>(__args)...))) + -> decltype( _VSTD::invoke(_VSTD::forward<_Args>(__args)...)) + { return _VSTD::invoke(_VSTD::forward<_Args>(__args)...); } }; -template, _Fn>, - is_move_constructible>, - is_constructible, _Args>..., - is_move_constructible>... - >::value>> -constexpr auto bind_front(_Fn&& __f, _Args&&... __args) -{ - return __perfect_forward<__bind_front_op, _Fn, _Args...>(_VSTD::forward<_Fn>(__f), - _VSTD::forward<_Args>(__args)...); +template +struct __bind_front_t : __perfect_forward<__bind_front_op, _Fn, _BoundArgs...> { + using __perfect_forward<__bind_front_op, _Fn, _BoundArgs...>::__perfect_forward; +}; + +template , _Fn>, + is_move_constructible>, + is_constructible, _Args>..., + is_move_constructible>... + >::value +>> +_LIBCPP_HIDE_FROM_ABI +constexpr auto bind_front(_Fn&& __f, _Args&&... __args) { + return __bind_front_t, decay_t<_Args>...>(_VSTD::forward<_Fn>(__f), _VSTD::forward<_Args>(__args)...); } #endif // _LIBCPP_STD_VER > 17 diff --git a/libcxx/include/__functional/not_fn.h b/libcxx/include/__functional/not_fn.h --- a/libcxx/include/__functional/not_fn.h +++ b/libcxx/include/__functional/not_fn.h @@ -23,21 +23,27 @@ #if _LIBCPP_STD_VER > 14 -struct __not_fn_op -{ - template - static _LIBCPP_CONSTEXPR_AFTER_CXX17 auto __call(_Args&&... __args) - noexcept(noexcept(!_VSTD::invoke(_VSTD::forward<_Args>(__args)...))) - -> decltype( !_VSTD::invoke(_VSTD::forward<_Args>(__args)...)) - { return !_VSTD::invoke(_VSTD::forward<_Args>(__args)...); } +struct __not_fn_op { + template + _LIBCPP_HIDE_FROM_ABI + _LIBCPP_CONSTEXPR_AFTER_CXX17 auto operator()(_Args&&... __args) const + noexcept(noexcept(!_VSTD::invoke(_VSTD::forward<_Args>(__args)...))) + -> decltype( !_VSTD::invoke(_VSTD::forward<_Args>(__args)...)) + { return !_VSTD::invoke(_VSTD::forward<_Args>(__args)...); } }; -template, _Fn> && - is_move_constructible_v<_Fn>>> -_LIBCPP_CONSTEXPR_AFTER_CXX17 auto not_fn(_Fn&& __f) -{ - return __perfect_forward<__not_fn_op, _Fn>(_VSTD::forward<_Fn>(__f)); +template +struct __not_fn_t : __perfect_forward<__not_fn_op, _Fn> { + using __perfect_forward<__not_fn_op, _Fn>::__perfect_forward; +}; + +template , _Fn> && + is_move_constructible_v> +>> +_LIBCPP_HIDE_FROM_ABI +_LIBCPP_CONSTEXPR_AFTER_CXX17 auto not_fn(_Fn&& __f) { + return __not_fn_t>(_VSTD::forward<_Fn>(__f)); } #endif // _LIBCPP_STD_VER > 14 diff --git a/libcxx/include/__functional/perfect_forward.h b/libcxx/include/__functional/perfect_forward.h --- a/libcxx/include/__functional/perfect_forward.h +++ b/libcxx/include/__functional/perfect_forward.h @@ -11,9 +11,11 @@ #define _LIBCPP___FUNCTIONAL_PERFECT_FORWARD_H #include <__config> +#include <__utility/declval.h> +#include <__utility/forward.h> +#include <__utility/move.h> #include #include -#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) #pragma GCC system_header @@ -23,63 +25,68 @@ #if _LIBCPP_STD_VER > 14 -template::value>::type> +template struct __perfect_forward_impl; -template -struct __perfect_forward_impl<_Op, __tuple_types<_Bound...>, __tuple_indices<_Idxs...>> -{ +template +struct __perfect_forward_impl<_Op, index_sequence<_Idx...>, _Bound...> { +private: tuple<_Bound...> __bound_; - template - _LIBCPP_INLINE_VISIBILITY constexpr auto operator()(_Args&&... __args) & - noexcept(noexcept(_Op::__call(_VSTD::get<_Idxs>(__bound_)..., _VSTD::forward<_Args>(__args)...))) - -> decltype( _Op::__call(_VSTD::get<_Idxs>(__bound_)..., _VSTD::forward<_Args>(__args)...)) - {return _Op::__call(_VSTD::get<_Idxs>(__bound_)..., _VSTD::forward<_Args>(__args)...);} - - template - _LIBCPP_INLINE_VISIBILITY constexpr auto operator()(_Args&&... __args) const& - noexcept(noexcept(_Op::__call(_VSTD::get<_Idxs>(__bound_)..., _VSTD::forward<_Args>(__args)...))) - -> decltype( _Op::__call(_VSTD::get<_Idxs>(__bound_)..., _VSTD::forward<_Args>(__args)...)) - {return _Op::__call(_VSTD::get<_Idxs>(__bound_)..., _VSTD::forward<_Args>(__args)...);} - - template - _LIBCPP_INLINE_VISIBILITY constexpr auto operator()(_Args&&... __args) && - noexcept(noexcept(_Op::__call(_VSTD::get<_Idxs>(_VSTD::move(__bound_))..., - _VSTD::forward<_Args>(__args)...))) - -> decltype( _Op::__call(_VSTD::get<_Idxs>(_VSTD::move(__bound_))..., - _VSTD::forward<_Args>(__args)...)) - {return _Op::__call(_VSTD::get<_Idxs>(_VSTD::move(__bound_))..., - _VSTD::forward<_Args>(__args)...);} - - template - _LIBCPP_INLINE_VISIBILITY constexpr auto operator()(_Args&&... __args) const&& - noexcept(noexcept(_Op::__call(_VSTD::get<_Idxs>(_VSTD::move(__bound_))..., - _VSTD::forward<_Args>(__args)...))) - -> decltype( _Op::__call(_VSTD::get<_Idxs>(_VSTD::move(__bound_))..., - _VSTD::forward<_Args>(__args)...)) - {return _Op::__call(_VSTD::get<_Idxs>(_VSTD::move(__bound_))..., - _VSTD::forward<_Args>(__args)...);} - - template>::type, - class = _EnableIf>> - constexpr __perfect_forward_impl(__perfect_forward_impl const& __other) - : __bound_(__other.__bound_) {} - - template>::type, - class = _EnableIf>> - constexpr __perfect_forward_impl(__perfect_forward_impl && __other) - : __bound_(_VSTD::move(__other.__bound_)) {} - - template - explicit constexpr __perfect_forward_impl(_BoundArgs&&... __bound) : - __bound_(_VSTD::forward<_BoundArgs>(__bound)...) { } +public: + template , _BoundArgs&&...> + >> + explicit constexpr __perfect_forward_impl(_BoundArgs&& ...__bound) + : __bound_(_VSTD::forward<_BoundArgs>(__bound)...) + { } + + __perfect_forward_impl(__perfect_forward_impl const&) = default; + __perfect_forward_impl(__perfect_forward_impl&&) = default; + + __perfect_forward_impl& operator=(__perfect_forward_impl const&) = default; + __perfect_forward_impl& operator=(__perfect_forward_impl&&) = default; + + template >> + _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Args&&... __args) & + noexcept(noexcept(_Op()(_VSTD::get<_Idx>(__bound_)..., _VSTD::forward<_Args>(__args)...))) + -> decltype( _Op()(_VSTD::get<_Idx>(__bound_)..., _VSTD::forward<_Args>(__args)...)) + { return _Op()(_VSTD::get<_Idx>(__bound_)..., _VSTD::forward<_Args>(__args)...); } + + template >> + auto operator()(_Args&&...) & = delete; + + template >> + _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Args&&... __args) const& + noexcept(noexcept(_Op()(_VSTD::get<_Idx>(__bound_)..., _VSTD::forward<_Args>(__args)...))) + -> decltype( _Op()(_VSTD::get<_Idx>(__bound_)..., _VSTD::forward<_Args>(__args)...)) + { return _Op()(_VSTD::get<_Idx>(__bound_)..., _VSTD::forward<_Args>(__args)...); } + + template >> + auto operator()(_Args&&...) const& = delete; + + template >> + _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Args&&... __args) && + noexcept(noexcept(_Op()(_VSTD::get<_Idx>(_VSTD::move(__bound_))..., _VSTD::forward<_Args>(__args)...))) + -> decltype( _Op()(_VSTD::get<_Idx>(_VSTD::move(__bound_))..., _VSTD::forward<_Args>(__args)...)) + { return _Op()(_VSTD::get<_Idx>(_VSTD::move(__bound_))..., _VSTD::forward<_Args>(__args)...); } + + template >> + auto operator()(_Args&&...) && = delete; + + template >> + _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Args&&... __args) const&& + noexcept(noexcept(_Op()(_VSTD::get<_Idx>(_VSTD::move(__bound_))..., _VSTD::forward<_Args>(__args)...))) + -> decltype( _Op()(_VSTD::get<_Idx>(_VSTD::move(__bound_))..., _VSTD::forward<_Args>(__args)...)) + { return _Op()(_VSTD::get<_Idx>(_VSTD::move(__bound_))..., _VSTD::forward<_Args>(__args)...); } + + template >> + auto operator()(_Args&&...) const&& = delete; }; -template -using __perfect_forward = - __perfect_forward_impl<_Op, __tuple_types...>>; +// __perfect_forward implements a perfect-forwarding call wrapper as explained in [func.require]. +template +using __perfect_forward = __perfect_forward_impl<_Op, index_sequence_for<_Args...>, _Args...>; #endif // _LIBCPP_STD_VER > 14 diff --git a/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp b/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp --- a/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp +++ b/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp @@ -10,32 +10,18 @@ // functional -// template constexpr unspecified bind_front(F&&, Args&&...); +// template +// constexpr unspecified bind_front(F&&, Args&&...); #include +#include +#include +#include +#include #include "callable_types.h" #include "test_macros.h" -constexpr int add(int a, int b) { return a + b; } - -constexpr int long_test(int a, int b, int c, int d, int e, int f) { - return a + b + c + d + e + f; -} - -struct Foo { - int a; - int b; -}; - -struct FooCall { - constexpr Foo operator()(int a, int b) { return Foo{a, b}; } -}; - -struct S { - constexpr bool operator()(int a) { return a == 1; } -}; - struct CopyMoveInfo { enum { none, copy, move } copy_kind; @@ -44,252 +30,376 @@ constexpr CopyMoveInfo(CopyMoveInfo&&) : copy_kind(move) {} }; -constexpr bool wasCopied(CopyMoveInfo info) { - return info.copy_kind == CopyMoveInfo::copy; -} -constexpr bool wasMoved(CopyMoveInfo info) { - return info.copy_kind == CopyMoveInfo::move; -} - -constexpr void basic_tests() { - int n = 2; - int m = 1; - - auto a = std::bind_front(add, m, n); - assert(a() == 3); - - auto b = std::bind_front(long_test, m, n, m, m, m, m); - assert(b() == 7); - - auto c = std::bind_front(long_test, n, m); - assert(c(1, 1, 1, 1) == 7); - - auto d = std::bind_front(S{}, m); - assert(d()); - - auto f = std::bind_front(add, n); - assert(f(3) == 5); +template +struct is_bind_frontable { + template + static auto test(int) + -> decltype((void)std::bind_front(std::declval()...), std::true_type()); - auto g = std::bind_front(add, n, 1); - assert(g() == 3); + template + static std::false_type test(...); - auto h = std::bind_front(long_test, 1, 1, 1); - assert(h(2, 2, 2) == 9); + static constexpr bool value = decltype(test(0))::value; +}; - // Make sure the arg is passed by value. - auto i = std::bind_front(add, n, 1); - n = 100; - assert(i() == 3); +struct NotCopyMove { + NotCopyMove() = delete; + NotCopyMove(const NotCopyMove&) = delete; + NotCopyMove(NotCopyMove&&) = delete; + template + void operator()(Args&& ...) const { } +}; - CopyMoveInfo info; - auto copied = std::bind_front(wasCopied, info); - assert(copied()); +struct NonConstCopyConstructible { + explicit NonConstCopyConstructible() {} + NonConstCopyConstructible(NonConstCopyConstructible&) {} +}; - auto moved = std::bind_front(wasMoved, info); - assert(std::move(moved)()); -} +struct MoveConstructible { + explicit MoveConstructible() {} + MoveConstructible(MoveConstructible&&) {} +}; -struct variadic_fn { - template - constexpr int operator()(Args&&... args) { - return sizeof...(args); +struct MakeTuple { + template + constexpr auto operator()(Args&& ...args) const { + return std::make_tuple(std::forward(args)...); } }; -constexpr void test_variadic() { - variadic_fn value; - auto fn = std::bind_front(value, 0, 0, 0); - assert(fn(0, 0, 0) == 6); -} +template +struct Elem { + template + constexpr bool operator==(Elem const&) const + { return X == Y; } +}; -struct mutable_callable { - bool should_call_const; +constexpr bool test() { + // 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>{})); + } + } - constexpr bool operator()(int, int) { - assert(!should_call_const); - return true; + // 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>{})); + } } - constexpr bool operator()(int, int) const { - assert(should_call_const); - return true; + + // 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>{})); + } } -}; -constexpr void test_mutable() { - const mutable_callable v1{true}; - const auto fn1 = std::bind_front(v1, 0); - assert(fn1(0)); + // Basic tests with fundamental types + { + int n = 2; + int m = 1; + auto add = [](int x, int y) { return x + y; }; + auto addN = [](int a, int b, int c, int d, int e, int f) { + return a + b + c + d + e + f; + }; - mutable_callable v2{false}; - auto fn2 = std::bind_front(v2, 0); - assert(fn2(0)); -}; + auto a = std::bind_front(add, m, n); + assert(a() == 3); -struct call_member { - constexpr bool member(int, int) { return true; } -}; + auto b = std::bind_front(addN, m, n, m, m, m, m); + assert(b() == 7); -constexpr void test_call_member() { - call_member value; - auto fn = std::bind_front(&call_member::member, value, 0); - assert(fn(0)); -} + auto c = std::bind_front(addN, n, m); + assert(c(1, 1, 1, 1) == 7); -struct no_const_lvalue { - constexpr void operator()(int) && {}; -}; + auto f = std::bind_front(add, n); + assert(f(3) == 5); -constexpr auto make_no_const_lvalue(int x) { - // This is to test that bind_front works when something like the following would not: - // return [nc = no_const_lvalue{}, x] { return nc(x); }; - // Above would not work because it would look for a () const & overload. - return std::bind_front(no_const_lvalue{}, x); -} + auto g = std::bind_front(add, n, 1); + assert(g() == 3); -constexpr void test_no_const_lvalue() { make_no_const_lvalue(1)(); } + auto h = std::bind_front(addN, 1, 1, 1); + assert(h(2, 2, 2) == 9); + } -constexpr void constructor_tests() { + // Make sure we don't treat std::reference_wrapper specially. { - MoveOnlyCallable value(true); - using RetT = decltype(std::bind_front(std::move(value), 1)); - - static_assert(std::is_move_constructible::value); - static_assert(!std::is_copy_constructible::value); - static_assert(!std::is_move_assignable::value); - static_assert(!std::is_copy_assignable::value); - - auto ret = std::bind_front(std::move(value), 1); - assert(ret()); - assert(ret(1, 2, 3)); - - auto ret1 = std::move(ret); - assert(!ret()); - assert(ret1()); - assert(ret1(1, 2, 3)); + auto add = [](std::reference_wrapper a, std::reference_wrapper b) { + return a.get() + b.get(); + }; + int i = 1, j = 2; + auto f = std::bind_front(add, std::ref(i)); + assert(f(std::ref(j)) == 3); } + + // Make sure we can call a function that's a pointer to a member function. { - CopyCallable value(true); - using RetT = decltype(std::bind_front(value, 1)); - - static_assert(std::is_move_constructible::value); - static_assert(std::is_copy_constructible::value); - static_assert(!std::is_move_assignable::value); - static_assert(!std::is_copy_assignable::value); - - auto ret = std::bind_front(value, 1); - assert(ret()); - assert(ret(1, 2, 3)); - - auto ret1 = std::move(ret); - assert(ret1()); - assert(ret1(1, 2, 3)); - - auto ret2 = std::bind_front(std::move(value), 1); - assert(!ret()); - assert(ret2()); - assert(ret2(1, 2, 3)); + struct MemberFunction { + constexpr bool foo(int, int) { return true; } + }; + MemberFunction value; + auto fn = std::bind_front(&MemberFunction::foo, value, 0); + assert(fn(0)); } - { - CopyAssignableWrapper value(true); - using RetT = decltype(std::bind_front(value, 1)); - static_assert(std::is_move_constructible::value); - static_assert(std::is_copy_constructible::value); - static_assert(std::is_move_assignable::value); - static_assert(std::is_copy_assignable::value); - } + // Make sure that we copy the bound arguments into the unspecified-type. { - MoveAssignableWrapper value(true); - using RetT = decltype(std::bind_front(std::move(value), 1)); - - static_assert(std::is_move_constructible::value); - static_assert(!std::is_copy_constructible::value); - static_assert(std::is_move_assignable::value); - static_assert(!std::is_copy_assignable::value); + auto add = [](int x, int y) { return x + y; }; + int n = 2; + auto i = std::bind_front(add, n, 1); + n = 100; + assert(i() == 3); } -} - -template -constexpr void test_return(F&& value, Args&&... args) { - auto ret = - std::bind_front(std::forward(value), std::forward(args)...); - static_assert(std::is_same::value); -} -constexpr void test_return_types() { - test_return(FooCall{}, 1, 2); - test_return(S{}, 1); - test_return(add, 2, 2); -} - -constexpr void test_arg_count() { - using T = decltype(std::bind_front(add, 1)); - static_assert(!std::is_invocable::value); - static_assert(std::is_invocable::value); -} - -template -struct is_bind_frontable { - template - static auto test(int) - -> decltype((void)std::bind_front(std::declval()...), - std::true_type()); - - template - static std::false_type test(...); - - static constexpr bool value = decltype(test(0))::value; -}; + // Make sure we pass the bound arguments to the function object + // with the right value category. + { + { + auto wasCopied = [](CopyMoveInfo info) { + return info.copy_kind == CopyMoveInfo::copy; + }; + CopyMoveInfo info; + auto copied = std::bind_front(wasCopied, info); + assert(copied()); + } + + { + auto wasMoved = [](CopyMoveInfo info) { + return info.copy_kind == CopyMoveInfo::move; + }; + CopyMoveInfo info; + auto moved = std::bind_front(wasMoved, info); + assert(std::move(moved)()); + } + } -struct NotCopyMove { - NotCopyMove() = delete; - NotCopyMove(const NotCopyMove&) = delete; - NotCopyMove(NotCopyMove&&) = delete; - void operator()() {} -}; + // Make sure we call the correctly cv-ref qualified operator() based on the + // value category of the bind_front unspecified-type. + { + struct F { + constexpr int operator()() & { return 1; } + constexpr int operator()() const& { return 2; } + constexpr int operator()() && { return 3; } + constexpr int operator()() const&& { return 4; } + }; + auto x = std::bind_front(F{}); + using X = decltype(x); + assert(static_cast(x)() == 1); + assert(static_cast(x)() == 2); + assert(static_cast(x)() == 3); + assert(static_cast(x)() == 4); + } -struct NonConstCopyConstructible { - explicit NonConstCopyConstructible() {} - NonConstCopyConstructible(NonConstCopyConstructible&) {} -}; + // Make sure the bind_front unspecified-type is NOT invocable when the call would select a + // differently-qualified operator(). + // + // For example, if the call to `operator()() &` is ill-formed, the call to the unspecified-type + // should be ill-formed and not fall back to the `operator()() const&` overload. + { + // Make sure we delete the & overload when the underlying call isn't valid + { + struct F { + void operator()() & = delete; + void operator()() const&; + void operator()() &&; + void operator()() const&&; + }; + using X = decltype(std::bind_front(F{})); + static_assert(!std::is_invocable_v); + static_assert( std::is_invocable_v); + static_assert( std::is_invocable_v); + static_assert( std::is_invocable_v); + } + + // There's no way to make sure we delete the const& overload when the underlying call isn't valid, + // so we can't check this one. + + // Make sure we delete the && overload when the underlying call isn't valid + { + struct F { + void operator()() &; + void operator()() const&; + void operator()() && = delete; + void operator()() const&&; + }; + using X = decltype(std::bind_front(F{})); + static_assert( std::is_invocable_v); + static_assert( std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert( std::is_invocable_v); + } + + // Make sure we delete the const&& overload when the underlying call isn't valid + { + struct F { + void operator()() &; + void operator()() const&; + void operator()() &&; + void operator()() const&& = delete; + }; + using X = decltype(std::bind_front(F{})); + static_assert( std::is_invocable_v); + static_assert( std::is_invocable_v); + static_assert( std::is_invocable_v); + static_assert(!std::is_invocable_v); + } + } -struct MoveConstructible { - explicit MoveConstructible() {} - MoveConstructible(MoveConstructible&&) {} -}; + // Some examples by Tim Song + { + { + struct T { }; + struct F { + void operator()(T&&) const &; + void operator()(T&&) && = delete; + }; + using X = decltype(std::bind_front(F{})); + static_assert(!std::is_invocable_v); + } + + { + struct T { }; + struct F { + void operator()(T const&) const; + void operator()(T&&) const = delete; + }; + using X = decltype(std::bind_front(F{}, T{})); + static_assert(!std::is_invocable_v); + } + } -constexpr void test_invocability() { - static_assert(!std::is_constructible_v); - static_assert(!std::is_move_constructible_v); - static_assert(!is_bind_frontable::value); - static_assert(!is_bind_frontable::value); - - static_assert( - !std::is_constructible_v); - static_assert(std::is_move_constructible_v); - static_assert(is_bind_frontable::value); - static_assert( - !is_bind_frontable::value); - - static_assert(std::is_constructible_v); - static_assert(!std::is_move_constructible_v); - static_assert( - !is_bind_frontable::value); - static_assert( - !is_bind_frontable::value); -} + // Test properties of the constructor of the unspecified-type returned by bind_front. + { + { + MoveOnlyCallable value(true); + using RetT = decltype(std::bind_front(std::move(value), 1)); + + static_assert( std::is_move_constructible::value); + static_assert(!std::is_copy_constructible::value); + static_assert(!std::is_move_assignable::value); + static_assert(!std::is_copy_assignable::value); + + auto ret = std::bind_front(std::move(value), 1); + assert(ret()); + assert(ret(1, 2, 3)); + + auto ret1 = std::move(ret); + assert(!ret()); + assert(ret1()); + assert(ret1(1, 2, 3)); + } + { + CopyCallable value(true); + using RetT = decltype(std::bind_front(value, 1)); + + static_assert( std::is_move_constructible::value); + static_assert( std::is_copy_constructible::value); + static_assert(!std::is_move_assignable::value); + static_assert(!std::is_copy_assignable::value); + + auto ret = std::bind_front(value, 1); + assert(ret()); + assert(ret(1, 2, 3)); + + auto ret1 = std::move(ret); + assert(ret1()); + assert(ret1(1, 2, 3)); + + auto ret2 = std::bind_front(std::move(value), 1); + assert(!ret()); + assert(ret2()); + assert(ret2(1, 2, 3)); + } + { + CopyAssignableWrapper value(true); + using RetT = decltype(std::bind_front(value, 1)); + + static_assert(std::is_move_constructible::value); + static_assert(std::is_copy_constructible::value); + static_assert(std::is_move_assignable::value); + static_assert(std::is_copy_assignable::value); + } + { + MoveAssignableWrapper value(true); + using RetT = decltype(std::bind_front(std::move(value), 1)); + + static_assert( std::is_move_constructible::value); + static_assert(!std::is_copy_constructible::value); + static_assert( std::is_move_assignable::value); + static_assert(!std::is_copy_assignable::value); + } + } -constexpr bool test() { - basic_tests(); - constructor_tests(); - test_return_types(); - test_arg_count(); - test_variadic(); - test_mutable(); - test_call_member(); - test_no_const_lvalue(); - test_invocability(); + // Make sure bind_front is SFINAE friendly + { + using T = decltype(std::bind_front(std::declval(), 1)); + static_assert(!std::is_invocable::value); + static_assert( std::is_invocable::value); + static_assert(!std::is_invocable::value); + static_assert(!std::is_invocable::value); + + static_assert(!std::is_constructible_v); + static_assert(!std::is_move_constructible_v); + static_assert(!is_bind_frontable::value); + static_assert(!is_bind_frontable::value); + + auto takeAnything = [](auto&& ...) { }; + static_assert(!std::is_constructible_v); + static_assert( std::is_move_constructible_v); + static_assert( is_bind_frontable::value); + static_assert(!is_bind_frontable::value); + + static_assert( std::is_constructible_v); + static_assert(!std::is_move_constructible_v); + static_assert(!is_bind_frontable::value); + static_assert(!is_bind_frontable::value); + } return true; } diff --git a/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.verify.cpp b/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.verify.cpp --- a/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.verify.cpp +++ b/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.verify.cpp @@ -10,7 +10,8 @@ // functional -// template constexpr unspecified bind_front(F&&, Args&&...); +// template +// constexpr unspecified bind_front(F&&, Args&&...); #include