Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<functional>: Fixes around not_fn #4057

Merged
merged 2 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 101 additions & 4 deletions stl/inc/functional
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,23 @@ _NODISCARD _CONSTEXPR20 _Mem_fn<_Rx _Ty::*> mem_fn(_Rx _Ty::*_Pm) noexcept {
}

#if _HAS_CXX17
#if _HAS_CXX20
#ifdef __cpp_lib_concepts // TRANSITION, GH-395
template <class _Callable, class... _Types>
concept _Can_invoke_then_negate = requires(
_Callable&& _Fx, _Types&&... _Args) { !_STD invoke(_STD forward<_Callable>(_Fx), _STD forward<_Types>(_Args)...); };
#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv
template <class _Void, class _Callable, class... _Types>
inline constexpr bool _Can_invoke_then_negate_impl = false;
template <class _Callable, class... _Types>
inline constexpr bool _Can_invoke_then_negate_impl<
void_t<decltype(!_STD invoke(_STD declval<_Callable>(), _STD declval<_Types>()...))>, _Callable, _Types...> = true;

template <class _Callable, class... _Types>
inline constexpr bool _Can_invoke_then_negate = _Can_invoke_then_negate_impl<void, _Callable, _Types...>;
#endif // ^^^ !defined(__cpp_lib_concepts) ^^^
#endif // _HAS_CXX20

struct _Not_fn_tag {
explicit _Not_fn_tag() = default;
};
Expand All @@ -603,39 +620,119 @@ public:
constexpr _Not_fn(const _Not_fn&) = default;
constexpr _Not_fn(_Not_fn&&) = default;

#if _HAS_CXX20 && defined(__cpp_lib_concepts) // TRANSITION, GH-395
template <class... _Types>
requires _Can_invoke_then_negate<_Decayed&, _Types...>
constexpr decltype(auto) operator()(_Types&&... _Args) & noexcept(
noexcept(!_STD invoke(this->_Get_val(), _STD forward<_Types>(_Args)...))) {
return !_STD invoke(this->_Get_val(), _STD forward<_Types>(_Args)...);
}

template <class... _Types>
requires _Can_invoke_then_negate<const _Decayed&, _Types...>
constexpr decltype(auto) operator()(_Types&&... _Args) const& noexcept(
noexcept(!_STD invoke(this->_Get_val(), _STD forward<_Types>(_Args)...))) {
return !_STD invoke(this->_Get_val(), _STD forward<_Types>(_Args)...);
}

template <class... _Types>
requires _Can_invoke_then_negate<_Decayed, _Types...>
constexpr decltype(auto) operator()(_Types&&... _Args) && noexcept(
noexcept(!_STD invoke(_STD move(this->_Get_val()), _STD forward<_Types>(_Args)...))) {
return !_STD invoke(_STD move(this->_Get_val()), _STD forward<_Types>(_Args)...);
}

template <class... _Types>
requires _Can_invoke_then_negate<const _Decayed, _Types...>
constexpr decltype(auto) operator()(_Types&&... _Args) const&& noexcept(
noexcept(!_STD invoke(_STD move(this->_Get_val()), _STD forward<_Types>(_Args)...))) {
return !_STD invoke(_STD move(this->_Get_val()), _STD forward<_Types>(_Args)...);
}

template <class... _Types>
void operator()(_Types&&...) & = delete;

template <class... _Types>
void operator()(_Types&&...) const& = delete;

template <class... _Types>
void operator()(_Types&&...) && = delete;

template <class... _Types>
void operator()(_Types&&...) const&& = delete;
#elif _HAS_CXX20 // ^^^ _HAS_CXX20 && defined(__cpp_lib_concepts) / _HAS_CXX20 && !defined(__cpp_lib_concepts) vvv
template <class... _Types, enable_if_t<_Can_invoke_then_negate<_Decayed&, _Types...>, int> = 0>
constexpr decltype(auto) operator()(_Types&&... _Args) & noexcept(
noexcept(!_STD invoke(this->_Get_val(), _STD forward<_Types>(_Args)...))) {
return !_STD invoke(this->_Get_val(), _STD forward<_Types>(_Args)...);
}

template <class... _Types, enable_if_t<_Can_invoke_then_negate<const _Decayed&, _Types...>, int> = 0>
constexpr decltype(auto) operator()(_Types&&... _Args) const& noexcept(
noexcept(!_STD invoke(this->_Get_val(), _STD forward<_Types>(_Args)...))) {
return !_STD invoke(this->_Get_val(), _STD forward<_Types>(_Args)...);
}

template <class... _Types, enable_if_t<_Can_invoke_then_negate<_Decayed, _Types...>, int> = 0>
constexpr decltype(auto) operator()(_Types&&... _Args) && noexcept(
noexcept(!_STD invoke(_STD move(this->_Get_val()), _STD forward<_Types>(_Args)...))) {
return !_STD invoke(_STD move(this->_Get_val()), _STD forward<_Types>(_Args)...);
}

template <class... _Types, enable_if_t<_Can_invoke_then_negate<const _Decayed, _Types...>, int> = 0>
constexpr decltype(auto) operator()(_Types&&... _Args) const&& noexcept(
noexcept(!_STD invoke(_STD move(this->_Get_val()), _STD forward<_Types>(_Args)...))) {
return !_STD invoke(_STD move(this->_Get_val()), _STD forward<_Types>(_Args)...);
}

template <class... _Types, enable_if_t<!_Can_invoke_then_negate<_Decayed&, _Types...>, int> = 0>
void operator()(_Types&&...) & = delete;

template <class... _Types, enable_if_t<!_Can_invoke_then_negate<const _Decayed&, _Types...>, int> = 0>
void operator()(_Types&&...) const& = delete;

template <class... _Types, enable_if_t<!_Can_invoke_then_negate<_Decayed, _Types...>, int> = 0>
void operator()(_Types&&...) && = delete;

template <class... _Types, enable_if_t<!_Can_invoke_then_negate<const _Decayed, _Types...>, int> = 0>
void operator()(_Types&&...) const&& = delete;
#else // ^^^ _HAS_CXX20 && !defined(__cpp_lib_concepts) / !_HAS_CXX20 vvv
template <class... _Types>
_CONSTEXPR20 auto operator()(_Types&&... _Args) & noexcept(
auto operator()(_Types&&... _Args) & noexcept(
noexcept(!_STD invoke(this->_Get_val(), _STD forward<_Types>(_Args)...)))
-> decltype(!_STD declval<invoke_result_t<_Decayed&, _Types...>>()) {
return !_STD invoke(this->_Get_val(), _STD forward<_Types>(_Args)...);
}

template <class... _Types>
_CONSTEXPR20 auto operator()(_Types&&... _Args) const& noexcept(
auto operator()(_Types&&... _Args) const& noexcept(
noexcept(!_STD invoke(this->_Get_val(), _STD forward<_Types>(_Args)...)))
-> decltype(!_STD declval<invoke_result_t<const _Decayed&, _Types...>>()) {
return !_STD invoke(this->_Get_val(), _STD forward<_Types>(_Args)...);
}

template <class... _Types>
_CONSTEXPR20 auto operator()(_Types&&... _Args) && noexcept(
auto operator()(_Types&&... _Args) && noexcept(
noexcept(!_STD invoke(_STD move(this->_Get_val()), _STD forward<_Types>(_Args)...)))
-> decltype(!_STD declval<invoke_result_t<_Decayed, _Types...>>()) {
return !_STD invoke(_STD move(this->_Get_val()), _STD forward<_Types>(_Args)...);
}

template <class... _Types>
_CONSTEXPR20 auto operator()(_Types&&... _Args) const&& noexcept(
auto operator()(_Types&&... _Args) const&& noexcept(
noexcept(!_STD invoke(_STD move(this->_Get_val()), _STD forward<_Types>(_Args)...)))
-> decltype(!_STD declval<invoke_result_t<const _Decayed, _Types...>>()) {
return !_STD invoke(_STD move(this->_Get_val()), _STD forward<_Types>(_Args)...);
}
#endif // ^^^ !_HAS_CXX20 ^^^
};

_EXPORT_STD template <class _Callable>
_NODISCARD _CONSTEXPR20 _Not_fn<decay_t<_Callable>> not_fn(_Callable&& _Obj) noexcept(
is_nothrow_constructible_v<decay_t<_Callable>, _Callable>) /* strengthened */ {
// wrap a callable object to be negated
static_assert(is_move_constructible_v<decay_t<_Callable>>,
"the callable type used with std::not_fn should be move constructible.");
return _Not_fn<decay_t<_Callable>>(_STD forward<_Callable>(_Obj), _Not_fn_tag{});
}
#endif // _HAS_CXX17
Expand Down
56 changes: 56 additions & 0 deletions tests/std/tests/Dev11_0535636_functional_overhaul/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2356,6 +2356,62 @@ _CONSTEXPR20 bool test_not_fn() {
return true;
}

// Also test the invocability of the return type of std::not_fn before and after the changes in P0356R5.
#if _HAS_CXX17
struct ConstOnlyFunctor {
bool operator()() = delete;
bool operator()() const {
return true;
}
};

struct ConstOnlyBooleanTester {
void operator()() {}
bool operator()() const {
return true;
}
};

struct GetPinnedNegatable {
struct PinnedNegatable {
PinnedNegatable() = default;

PinnedNegatable(const PinnedNegatable&) = delete;
PinnedNegatable& operator=(const PinnedNegatable&) = delete;

friend bool operator!(PinnedNegatable) {
return false;
}
};

PinnedNegatable operator()() const {
return {};
}
};

using NegatedConstOnlyFunctor = decltype(not_fn(ConstOnlyFunctor{}));
using NegatedConstOnlyBooleanTester = decltype(not_fn(ConstOnlyBooleanTester{}));
using NegatedGetPinnedNegatable = decltype(not_fn(GetPinnedNegatable{}));

#if _HAS_CXX20
constexpr bool not_fn_is_perfect_forwarding = true;
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
constexpr bool not_fn_is_perfect_forwarding = false;
#endif // ^^^ !_HAS_CXX20 ^^^

static_assert(is_invocable_v<const NegatedConstOnlyFunctor>);
static_assert(is_invocable_v<const NegatedConstOnlyFunctor&>);
static_assert(is_invocable_v<const NegatedConstOnlyBooleanTester>);
static_assert(is_invocable_v<const NegatedConstOnlyBooleanTester&>);

static_assert(is_invocable_v<NegatedConstOnlyFunctor> == !not_fn_is_perfect_forwarding);
static_assert(is_invocable_v<NegatedConstOnlyFunctor&> == !not_fn_is_perfect_forwarding);
static_assert(is_invocable_v<NegatedConstOnlyBooleanTester> == !not_fn_is_perfect_forwarding);
static_assert(is_invocable_v<NegatedConstOnlyBooleanTester&> == !not_fn_is_perfect_forwarding);

static_assert(is_invocable_v<NegatedGetPinnedNegatable> == not_fn_is_perfect_forwarding);
#endif // _HAS_CXX17

int main() {
// Test addressof() with functions.
assert(addressof(triple) == &triple);
Expand Down