//===----------------------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23 // // template constexpr unspecified not_fn() noexcept; #include #include #include #include #include #include #include "test_macros.h" class BooleanTestable { bool val_; public: constexpr explicit BooleanTestable(bool val) : val_(val) {} constexpr operator bool() const { return val_; } constexpr BooleanTestable operator!() const { return BooleanTestable{!val_}; } }; LIBCPP_STATIC_ASSERT(std::__boolean_testable); class FakeBool { int val_; public: constexpr FakeBool(int val) : val_(val) {} constexpr FakeBool operator!() const { return FakeBool{-val_}; } constexpr bool operator==(int other) const { return val_ == other; } }; template struct MaybeNoexceptFn { bool operator()() const noexcept(IsNoexcept); // not defined }; template struct MaybeNoexceptNegation { bool operator!() noexcept(IsNoexcept); // not defined }; template MaybeNoexceptNegation maybe_noexcept_negation() noexcept { return {}; } constexpr void basic_tests() { { // Test constant functions auto false_fn = std::not_fn(); assert(false_fn()); auto true_fn = std::not_fn(); assert(!true_fn()); static_assert(noexcept(std::not_fn())); static_assert(noexcept(std::not_fn())); } { // Test function with one argument auto is_odd = std::not_fn<[](auto x) { return x % 2 == 0; }>(); assert(is_odd(1)); assert(!is_odd(2)); assert(is_odd(3)); assert(!is_odd(4)); assert(is_odd(5)); } { // Test function with multiple arguments auto at_least_10 = [](auto... vals) { return (vals + ... + 0) >= 10; }; auto at_most_9 = std::not_fn(); assert(at_most_9()); assert(at_most_9(1)); assert(at_most_9(1, 2, 3, 4, -1)); assert(at_most_9(3, 3, 2, 1, -2)); assert(!at_most_9(10, -1, 2)); assert(!at_most_9(5, 5)); static_assert(noexcept(std::not_fn())); } { // Test function that returns boolean-testable type other than bool auto is_product_even = [](auto... vals) { return BooleanTestable{(vals * ... * 1) % 2 == 0}; }; auto is_product_odd = std::not_fn(); assert(is_product_odd()); assert(is_product_odd(1, 3, 5, 9)); assert(is_product_odd(3, 3, 3, 3)); assert(!is_product_odd(3, 5, 9, 11, 0)); assert(!is_product_odd(11, 7, 5, 3, 2)); static_assert(noexcept(std::not_fn())); } { // Test function that returns non-boolean-testable type auto sum = [](auto... vals) -> FakeBool { return (vals + ... + 0); }; auto negated_sum = std::not_fn(); assert(negated_sum() == 0); assert(negated_sum(3) == -3); assert(negated_sum(4, 5, 1, 3) == -13); assert(negated_sum(4, 2, 5, 6, 1) == -18); assert(negated_sum(-1, 3, 2, -8) == 4); static_assert(noexcept(std::not_fn())); } { // Test member pointers struct MemberPointerTester { bool value = true; constexpr bool not_value() const { return !value; } constexpr bool value_and(bool other) noexcept { return value && other; } }; MemberPointerTester tester; auto not_mem_object = std::not_fn<&MemberPointerTester::value>(); assert(!not_mem_object(tester)); assert(!not_mem_object(std::as_const(tester))); static_assert(noexcept(not_mem_object(tester))); static_assert(noexcept(not_mem_object(std::as_const(tester)))); auto not_nullary_mem_fn = std::not_fn<&MemberPointerTester::not_value>(); assert(not_nullary_mem_fn(tester)); static_assert(!noexcept(not_nullary_mem_fn(tester))); auto not_unary_mem_fn = std::not_fn<&MemberPointerTester::value_and>(); assert(not_unary_mem_fn(tester, false)); static_assert(noexcept(not_unary_mem_fn(tester, false))); static_assert(!std::is_invocable_v); } } constexpr void test_perfect_forwarding_call_wrapper() { { // Make sure we call the correctly cv-ref qualified operator() // based on the value category of the not_fn unspecified-type. struct X { constexpr FakeBool operator()() & { return 1; } constexpr FakeBool operator()() const& { return 2; } constexpr FakeBool operator()() && { return 3; } constexpr FakeBool operator()() const&& { return 4; } }; auto f = std::not_fn(); using F = decltype(f); assert(static_cast(f)() == -2); assert(static_cast(f)() == -2); assert(static_cast(f)() == -2); assert(static_cast(f)() == -2); } // Call to `not_fn` 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 { FakeBool operator()() & = delete; FakeBool operator()() const&; FakeBool operator()() &&; FakeBool operator()() const&&; }; using F = decltype(std::not_fn()); static_assert(std::invocable); static_assert(std::invocable); static_assert(std::invocable); static_assert(std::invocable); } { // Make sure unspecified-type is not callable when we delete the const& overload. struct X { FakeBool operator()() &; FakeBool operator()() const& = delete; FakeBool operator()() &&; FakeBool operator()() const&&; }; using F = decltype(std::not_fn()); static_assert(!std::invocable); static_assert(!std::invocable); static_assert(!std::invocable); static_assert(!std::invocable); } { // Make sure unspecified-type is still callable when we delete the && overload. struct X { FakeBool operator()() &; FakeBool operator()() const&; FakeBool operator()() && = delete; FakeBool operator()() const&&; }; using F = decltype(std::not_fn()); static_assert(std::invocable); static_assert(std::invocable); static_assert(std::invocable); static_assert(std::invocable); } { // Make sure unspecified-type is still callable when we delete the const&& overload. struct X { FakeBool operator()() &; FakeBool operator()() const&; FakeBool operator()() &&; FakeBool operator()() const&& = delete; }; using F = decltype(std::not_fn()); static_assert(std::invocable); static_assert(std::invocable); static_assert(std::invocable); static_assert(std::invocable); } } { // Test perfect forwarding auto f = [](int& val) { val = 5; return false; }; auto not_f = std::not_fn(); int val = 0; assert(not_f(val)); assert(val == 5); using NotF = decltype(not_f); static_assert(std::invocable); static_assert(!std::invocable); } } constexpr void test_return_type() { { // Test constructors and assignment operators struct IsPowerOfTwo { constexpr bool operator()(unsigned int x) const { return std::has_single_bit(x); } }; auto is_not_power_of_2 = std::not_fn(); assert(is_not_power_of_2(5)); assert(!is_not_power_of_2(4)); auto moved = std::move(is_not_power_of_2); assert(moved(5)); assert(!moved(4)); auto copied = is_not_power_of_2; assert(copied(7)); assert(!copied(8)); moved = std::move(copied); assert(copied(9)); assert(!copied(16)); copied = moved; assert(copied(11)); assert(!copied(32)); } { // Make sure `not_fn` unspecified-type's operator() is SFINAE-friendly. using F = decltype(std::not_fn<[](int x) { return !x; }>()); static_assert(!std::is_invocable::value); static_assert(std::is_invocable::value); static_assert(!std::is_invocable::value); static_assert(!std::is_invocable::value); } { // Test noexceptness auto always_noexcept = std::not_fn{}>(); static_assert(noexcept(always_noexcept())); auto never_noexcept = std::not_fn{}>(); static_assert(!noexcept(never_noexcept())); auto always_noexcept_negation = std::not_fn>(); static_assert(noexcept(always_noexcept_negation())); auto never_noexcept_negation = std::not_fn>(); static_assert(!noexcept(never_noexcept_negation())); } { // Test calling volatile wrapper using NotFn = decltype(std::not_fn()); static_assert(!std::invocable); static_assert(!std::invocable); static_assert(!std::invocable); static_assert(!std::invocable); } } constexpr bool test() { basic_tests(); test_perfect_forwarding_call_wrapper(); test_return_type(); return true; } int main(int, char**) { test(); static_assert(test()); return 0; }