mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-17 03:06:34 +00:00
[libc++] Implement std::not_fn<NTTP> (#86133)
Implement `std::not_fn<NTTP>` from "P2714R1 Bind front and back to NTTP callables".
This commit is contained in:
parent
c3910823c7
commit
c91d805e66
@ -458,6 +458,8 @@ Status
|
||||
---------------------------------------------------------- -----------------
|
||||
``__cpp_lib_mdspan`` ``202406L``
|
||||
---------------------------------------------------------- -----------------
|
||||
``__cpp_lib_not_fn`` ``202306L``
|
||||
---------------------------------------------------------- -----------------
|
||||
``__cpp_lib_optional_range_support`` *unimplemented*
|
||||
---------------------------------------------------------- -----------------
|
||||
``__cpp_lib_out_ptr`` ``202311L``
|
||||
|
@ -24,7 +24,7 @@
|
||||
"`P1383R2 <https://wg21.link/P1383R2>`__","More ``constexpr`` for ``<cmath>`` and ``<complex>``","2023-06 (Varna)","","",""
|
||||
"`P2734R0 <https://wg21.link/P2734R0>`__","Adding the new SI prefixes","2023-06 (Varna)","|Complete|","17",""
|
||||
"`P2548R6 <https://wg21.link/P2548R6>`__","``copyable_function``","2023-06 (Varna)","","",""
|
||||
"`P2714R1 <https://wg21.link/P2714R1>`__","Bind front and back to NTTP callables","2023-06 (Varna)","","",""
|
||||
"`P2714R1 <https://wg21.link/P2714R1>`__","Bind front and back to NTTP callables","2023-06 (Varna)","|Partial|","20","``not_fn`` only"
|
||||
"`P2630R4 <https://wg21.link/P2630R4>`__","``submdspan``","2023-06 (Varna)","","",""
|
||||
"","","","","",""
|
||||
"`P0543R3 <https://wg21.link/P0543R3>`__","Saturation arithmetic","2023-11 (Kona)","|Complete|","18",""
|
||||
|
|
@ -16,6 +16,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)
|
||||
@ -48,6 +50,27 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 auto not_fn(_Fn&& __f) {
|
||||
|
||||
#endif // _LIBCPP_STD_VER >= 17
|
||||
|
||||
#if _LIBCPP_STD_VER >= 26
|
||||
|
||||
template <auto _Fn>
|
||||
struct __nttp_not_fn_t {
|
||||
template <class... _Args>
|
||||
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Args&&... __args) const
|
||||
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>
|
||||
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI constexpr auto not_fn() noexcept {
|
||||
if constexpr (using _Ty = decltype(_Fn); is_pointer_v<_Ty> || is_member_pointer_v<_Ty>)
|
||||
static_assert(_Fn != nullptr, "f cannot be equal to nullptr");
|
||||
return __nttp_not_fn_t<_Fn>();
|
||||
}
|
||||
|
||||
#endif // _LIBCPP_STD_VER >= 26
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // _LIBCPP___FUNCTIONAL_NOT_FN_H
|
||||
|
@ -215,6 +215,8 @@ binary_negate<Predicate> not2(const Predicate& pred);
|
||||
|
||||
template <class F>
|
||||
constexpr unspecified not_fn(F&& f); // C++17, constexpr in C++20
|
||||
template <auto f>
|
||||
constexpr unspecified not_fn() noexcept; // C++26
|
||||
|
||||
// [func.bind.partial], function templates bind_front and bind_back
|
||||
template<class F, class... Args>
|
||||
|
@ -171,7 +171,8 @@ __cpp_lib_nonmember_container_access 201411L <array> <deque>
|
||||
<iterator> <list> <map>
|
||||
<regex> <set> <string>
|
||||
<unordered_map> <unordered_set> <vector>
|
||||
__cpp_lib_not_fn 201603L <functional>
|
||||
__cpp_lib_not_fn 202306L <functional>
|
||||
201603L // C++17
|
||||
__cpp_lib_null_iterators 201304L <iterator>
|
||||
__cpp_lib_optional 202110L <optional>
|
||||
202106L // C++20
|
||||
@ -557,6 +558,8 @@ __cpp_lib_void_t 201411L <type_traits>
|
||||
// # define __cpp_lib_linalg 202311L
|
||||
# undef __cpp_lib_mdspan
|
||||
# define __cpp_lib_mdspan 202406L
|
||||
# undef __cpp_lib_not_fn
|
||||
# define __cpp_lib_not_fn 202306L
|
||||
// # define __cpp_lib_optional_range_support 202406L
|
||||
# undef __cpp_lib_out_ptr
|
||||
# define __cpp_lib_out_ptr 202311L
|
||||
|
@ -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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
|
||||
|
||||
// <functional>
|
||||
|
||||
// Type of `std::not_fn<NTTP>()` is always empty.
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
struct NonEmptyFunctionObject {
|
||||
bool val = true;
|
||||
bool operator()() const;
|
||||
};
|
||||
|
||||
bool func();
|
||||
|
||||
struct SomeClass {
|
||||
bool member_object;
|
||||
bool member_function();
|
||||
};
|
||||
|
||||
using ResultWithEmptyFuncObject = decltype(std::not_fn<std::false_type{}>());
|
||||
static_assert(std::is_empty_v<ResultWithEmptyFuncObject>);
|
||||
|
||||
using ResultWithNotEmptyFuncObject = decltype(std::not_fn<NonEmptyFunctionObject{}>());
|
||||
static_assert(std::is_empty_v<ResultWithNotEmptyFuncObject>);
|
||||
|
||||
using ResultWithFunctionPointer = decltype(std::not_fn<&func>());
|
||||
static_assert(std::is_empty_v<ResultWithFunctionPointer>);
|
||||
|
||||
using ResultWithMemberObjectPointer = decltype(std::not_fn<&SomeClass::member_object>());
|
||||
static_assert(std::is_empty_v<ResultWithMemberObjectPointer>);
|
||||
|
||||
using ResultWithMemberFunctionPointer = decltype(std::not_fn<&SomeClass::member_function>());
|
||||
static_assert(std::is_empty_v<ResultWithMemberFunctionPointer>);
|
@ -0,0 +1,24 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
|
||||
// <functional>
|
||||
|
||||
// Test the libc++ extension that std::not_fn<NTTP> is marked as [[nodiscard]].
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
void test() {
|
||||
using F = std::true_type;
|
||||
std::not_fn<F{}>(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
|
||||
auto negated = std::not_fn<F{}>();
|
||||
negated(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
}
|
@ -27,6 +27,7 @@
|
||||
__cpp_lib_invoke_r 202106L [C++23]
|
||||
__cpp_lib_move_only_function 202110L [C++23]
|
||||
__cpp_lib_not_fn 201603L [C++17]
|
||||
202306L [C++26]
|
||||
__cpp_lib_ranges 202110L [C++20]
|
||||
202406L [C++23]
|
||||
__cpp_lib_reference_wrapper 202403L [C++26]
|
||||
@ -525,8 +526,8 @@
|
||||
# ifndef __cpp_lib_not_fn
|
||||
# error "__cpp_lib_not_fn should be defined in c++26"
|
||||
# endif
|
||||
# if __cpp_lib_not_fn != 201603L
|
||||
# error "__cpp_lib_not_fn should have the value 201603L in c++26"
|
||||
# if __cpp_lib_not_fn != 202306L
|
||||
# error "__cpp_lib_not_fn should have the value 202306L in c++26"
|
||||
# endif
|
||||
|
||||
# ifndef __cpp_lib_ranges
|
||||
|
@ -156,6 +156,7 @@
|
||||
__cpp_lib_node_extract 201606L [C++17]
|
||||
__cpp_lib_nonmember_container_access 201411L [C++17]
|
||||
__cpp_lib_not_fn 201603L [C++17]
|
||||
202306L [C++26]
|
||||
__cpp_lib_null_iterators 201304L [C++14]
|
||||
__cpp_lib_optional 201606L [C++17]
|
||||
202106L [C++20]
|
||||
@ -7405,8 +7406,8 @@
|
||||
# ifndef __cpp_lib_not_fn
|
||||
# error "__cpp_lib_not_fn should be defined in c++26"
|
||||
# endif
|
||||
# if __cpp_lib_not_fn != 201603L
|
||||
# error "__cpp_lib_not_fn should have the value 201603L in c++26"
|
||||
# if __cpp_lib_not_fn != 202306L
|
||||
# error "__cpp_lib_not_fn should have the value 202306L in c++26"
|
||||
# endif
|
||||
|
||||
# ifndef __cpp_lib_null_iterators
|
||||
|
@ -0,0 +1,310 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
|
||||
// <functional>
|
||||
|
||||
// template<auto f> constexpr unspecified not_fn() noexcept;
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <bit>
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#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<BooleanTestable>);
|
||||
|
||||
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 <bool IsNoexcept>
|
||||
struct MaybeNoexceptFn {
|
||||
bool operator()() const noexcept(IsNoexcept); // not defined
|
||||
};
|
||||
|
||||
template <bool IsNoexcept>
|
||||
struct MaybeNoexceptNegation {
|
||||
bool operator!() noexcept(IsNoexcept); // not defined
|
||||
};
|
||||
|
||||
template <bool IsNoexcept>
|
||||
MaybeNoexceptNegation<IsNoexcept> maybe_noexcept_negation() noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr void basic_tests() {
|
||||
{ // Test constant functions
|
||||
auto false_fn = std::not_fn<std::false_type{}>();
|
||||
assert(false_fn());
|
||||
|
||||
auto true_fn = std::not_fn<std::true_type{}>();
|
||||
assert(!true_fn());
|
||||
|
||||
static_assert(noexcept(std::not_fn<std::false_type{}>()));
|
||||
static_assert(noexcept(std::not_fn<std::true_type{}>()));
|
||||
}
|
||||
|
||||
{ // 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<at_least_10>();
|
||||
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<at_least_10>()));
|
||||
}
|
||||
|
||||
{ // 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<is_product_even>();
|
||||
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<is_product_even>()));
|
||||
}
|
||||
|
||||
{ // Test function that returns non-boolean-testable type
|
||||
auto sum = [](auto... vals) -> FakeBool { return (vals + ... + 0); };
|
||||
auto negated_sum = std::not_fn<sum>();
|
||||
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<sum>()));
|
||||
}
|
||||
|
||||
{ // 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<decltype(not_unary_mem_fn), const MemberPointerTester&, bool>);
|
||||
}
|
||||
}
|
||||
|
||||
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<NTTP> 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<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 `not_fn<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 {
|
||||
FakeBool operator()() & = delete;
|
||||
FakeBool operator()() const&;
|
||||
FakeBool operator()() &&;
|
||||
FakeBool operator()() const&&;
|
||||
};
|
||||
|
||||
using F = decltype(std::not_fn<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 {
|
||||
FakeBool operator()() &;
|
||||
FakeBool operator()() const& = delete;
|
||||
FakeBool operator()() &&;
|
||||
FakeBool operator()() const&&;
|
||||
};
|
||||
|
||||
using F = decltype(std::not_fn<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 {
|
||||
FakeBool operator()() &;
|
||||
FakeBool operator()() const&;
|
||||
FakeBool operator()() && = delete;
|
||||
FakeBool operator()() const&&;
|
||||
};
|
||||
|
||||
using F = decltype(std::not_fn<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 {
|
||||
FakeBool operator()() &;
|
||||
FakeBool operator()() const&;
|
||||
FakeBool operator()() &&;
|
||||
FakeBool operator()() const&& = delete;
|
||||
};
|
||||
|
||||
using F = decltype(std::not_fn<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
|
||||
auto f = [](int& val) {
|
||||
val = 5;
|
||||
return false;
|
||||
};
|
||||
|
||||
auto not_f = std::not_fn<f>();
|
||||
int val = 0;
|
||||
assert(not_f(val));
|
||||
assert(val == 5);
|
||||
|
||||
using NotF = decltype(not_f);
|
||||
static_assert(std::invocable<NotF, int&>);
|
||||
static_assert(!std::invocable<NotF, int>);
|
||||
}
|
||||
}
|
||||
|
||||
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<IsPowerOfTwo{}>();
|
||||
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<NTTP>` unspecified-type's operator() is SFINAE-friendly.
|
||||
using F = decltype(std::not_fn<[](int x) { return !x; }>());
|
||||
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::not_fn<MaybeNoexceptFn<true>{}>();
|
||||
static_assert(noexcept(always_noexcept()));
|
||||
|
||||
auto never_noexcept = std::not_fn<MaybeNoexceptFn<false>{}>();
|
||||
static_assert(!noexcept(never_noexcept()));
|
||||
|
||||
auto always_noexcept_negation = std::not_fn<maybe_noexcept_negation<true>>();
|
||||
static_assert(noexcept(always_noexcept_negation()));
|
||||
|
||||
auto never_noexcept_negation = std::not_fn<maybe_noexcept_negation<false>>();
|
||||
static_assert(!noexcept(never_noexcept_negation()));
|
||||
}
|
||||
|
||||
{ // Test calling volatile wrapper
|
||||
using NotFn = decltype(std::not_fn<std::false_type{}>());
|
||||
static_assert(!std::invocable<volatile NotFn&>);
|
||||
static_assert(!std::invocable<const volatile NotFn&>);
|
||||
static_assert(!std::invocable<volatile NotFn>);
|
||||
static_assert(!std::invocable<const volatile NotFn>);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
|
||||
// <functional>
|
||||
|
||||
// template<auto f> constexpr unspecified not_fn() noexcept;
|
||||
// Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
|
||||
|
||||
#include <functional>
|
||||
|
||||
struct X {};
|
||||
|
||||
void test() {
|
||||
auto not_fn1 = std::not_fn<static_cast<bool (*)()>(nullptr)>();
|
||||
// expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': f cannot be equal to nullptr}}
|
||||
|
||||
auto not_fn2 = std::not_fn<static_cast<bool X::*>(nullptr)>();
|
||||
// expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': f cannot be equal to nullptr}}
|
||||
|
||||
auto not_fn3 = std::not_fn<static_cast<bool (X::*)()>(nullptr)>();
|
||||
// expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': f cannot be equal to nullptr}}
|
||||
}
|
@ -931,7 +931,7 @@ feature_test_macros = [
|
||||
"name": "__cpp_lib_not_fn",
|
||||
"values": {
|
||||
"c++17": 201603,
|
||||
# "c++26": 202306, # P2714R1 Bind front and back to NTTP callables
|
||||
"c++26": 202306, # P2714R1 Bind front and back to NTTP callables
|
||||
},
|
||||
"headers": ["functional"],
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user