functional_not_fn

functional_not_fn (한국어 부제)

std::not_fn은 주어진 호출 가능 객체(Callable)의 결과를 논리적으로 부정하는 함수 래퍼를 만들어 주는 유틸리티예요. C++17에서 도입되었고, C++20부터 constexpr로 사용할 수 있으며, C++26부터는 호출 가능 객체를 넌타입 템플릿 인자로 직접 전달하는 오버로드도 추가되었어요. 이 페이지에서는 std::not_fn의 두 가지 형태와 반환 타입, 멤버 함수, 구현 예시, 사용 예제를 자세히 설명할게요.

출처: cppreference

본문

헤더

<functional> 헤더에 정의됨
template < class F > /* unspecified */ not_fn ( F && f ); (1) (since C++17) (constexpr since C++20)
template < auto ConstFn > constexpr /* unspecified */ not_fn () noexcept ; (2) (since C++26)

매개변수

f - 래퍼가 보관하는 Callable 객체를 구성하는 데 사용되는 객체
타입 요구 사항
- std::decay_t<F>는 Callable 및 MoveConstructible 요구 사항을 충족해야 해요.
- std::is_constructible_v<std::decay_t<F>, F>true여야 해요.

반환 값

std::not_fn 반환 타입

멤버 객체

std::not_fn의 반환 타입은 std::decay_t<F> 타입의 멤버 객체를 보관해요.

생성자

explicit T ( F && f ); (1) (since C++17) (constexpr since C++20) ( exposition only* )
T ( T && f ) = default ; T ( const T & f ) = default ; (2) (since C++17)
명시적으로 default로 정의된 생성자들은 반환 타입을 할당 불가능하게 만들어요. (until C++20)
이 생성자들이 명시적으로 default인지, 반환 타입이 할당 가능한지는 지정되지 않았어요. (since C++20)

멤버 함수 operator()

template < class ... Args > auto operator ()( Args && ... args ) & -> decltype ( ! std :: declval < std :: invoke_result_t < std :: decay_t < F >& , Args ... >> ()); template < class ... Args > auto operator ()( Args && ... args ) const & -> decltype ( ! std :: declval < std :: invoke_result_t < std :: decay_t < F > const & , Args ... >> ()); (1) (since C++17) (until C++20)
template < class ... Args > constexpr auto operator ()( Args && ... args ) & noexcept ( /* see below / ) -> decltype ( ! std :: invoke ( std :: declval < std :: decay_t < F >&> (), std :: declval < Args > ()...)); template < class ... Args > constexpr auto operator ()( Args && ... args ) const & noexcept ( / see below */ ) -> decltype ( ! std :: invoke ( std :: declval < std :: decay_t < F > const &> (), std :: declval < Args > ()...)); (since C++20)
template < class ... Args > auto operator ()( Args && ... args ) && -> decltype ( ! std :: declval < std :: invoke_result_t < std :: decay_t < F > , Args ... >> ()); template < class ... Args > auto operator ()( Args && ... args ) const && -> decltype ( ! std :: declval < std :: invoke_result_t < std :: decay_t < F > const , Args ... >> ()); (2) (since C++17) (until C++20)
template < class ... Args > constexpr auto operator ()( Args && ... args ) && noexcept ( /* see below / ) -> decltype ( ! std :: invoke ( std :: declval < std :: decay_t < F >> (), std :: declval < Args > ()...)); template < class ... Args > constexpr auto operator ()( Args && ... args ) const && noexcept ( / see below */ ) -> decltype ( ! std :: invoke ( std :: declval < std :: decay_t < F > const > (), std :: declval < Args > ()...)); (since C++20)

fdstd::decay_t<F> 타입의 멤버 객체라고 할게요.

1) return ! std::invoke(fd, std::forward<Args>(args)...); 와 동일해요. 2) return ! std::invoke(std::move(fd), std::forward<Args>(args)...); 와 동일해요. 결과를 호출하는 동안 원래 선택된 operator() 오버로드의 반환 타입에 대한 치환이 실패하면, 다른 오버로드가 선택될 수 있어요. (since C++17) (until C++20)
1) ! std::invoke(fd, std::forward<Args>(args)...) 와 표현-동등해요. 2) ! std::invoke(std::move(fd), std::forward<Args>(args)...) 와 표현-동등해요. 결과를 호출하는 동안 원래 선택된 operator() 오버로드의 반환 타입에 대한 치환이 실패하면, 그 호출은 ill-formed이며 이는 치환 실패로 간주될 수도 있어요. (since C++20)

std::not_fn 무상태 반환 타입

반환 타입은 CopyConstructible한 무상태 클래스예요. 반환 타입이 할당 가능한지는 지정되지 않았어요.

멤버 함수 operator()

template < class ... Args > constexpr auto operator ()( Args && ... args ) const noexcept ( /* see below */ ) -> decltype ( ! std :: invoke ( ConstFn , std :: declval < Args > ()...)); (since C++26)

! std::invoke(ConstFn, std::forward<Args>(args)...) 와 표현-동등해요.

예외

가능한 구현

(1) not_fn
namespace detail { template < class V , class F , class ... Args > constexpr bool negate_invocable_impl = false ; template < class F , class ... Args > constexpr bool negate_invocable_impl < std :: void_t < decltype ( ! std :: invoke ( std :: declval < F > (), std :: declval < Args > ()...)) > , F , Args ... > = true ; template < class F , class ... Args > constexpr bool negate_invocable_v = negate_invocable_impl < void , F , Args ... > ; template < class F > struct not_fn_t { F f ; template < class ... Args , std :: enable_if_t < negate_invocable_v < F & , Args ... > , int > = 0 > constexpr decltype ( auto ) operator ()( Args && ... args ) & noexcept ( noexcept ( ! std :: invoke ( f , std :: forward < Args > ( args )...))) { return ! std :: invoke ( f , std :: forward < Args > ( args )...); } template < class ... Args , std :: enable_if_t < negate_invocable_v < const F & , Args ... > , int > = 0 > constexpr decltype ( auto ) operator ()( Args && ... args ) const & noexcept ( noexcept ( ! std :: invoke ( f , std :: forward < Args > ( args )...))) { return ! std :: invoke ( f , std :: forward < Args > ( args )...); } template < class ... Args , std :: enable_if_t < negate_invocable_v < F , Args ... > , int > = 0 > constexpr decltype ( auto ) operator ()( Args && ... args ) && noexcept ( noexcept ( ! std :: invoke ( std :: move ( f ), std :: forward < Args > ( args )...))) { return ! std :: invoke ( std :: move ( f ), std :: forward < Args > ( args )...); } template < class ... Args , std :: enable_if_t < negate_invocable_v < const F , Args ... > , int > = 0 > constexpr decltype ( auto ) operator ()( Args && ... args ) const && noexcept ( noexcept ( ! std :: invoke ( std :: move ( f ), std :: forward < Args > ( args )...))) { return ! std :: invoke ( std :: move ( f ), std :: forward < Args > ( args )...); } // Deleted overloads are needed since C++20 // for preventing a non-equivalent but well-formed overload to be selected. template < class ... Args , std :: enable_if_t <! negate_invocable_v < F & , Args ... > , int > = 0 > void operator ()( Args && ...) & = delete ; template < class ... Args , std :: enable_if_t <! negate_invocable_v < const F & , Args ... > , int > = 0 > void operator ()( Args && ...) const & = delete ; template < class ... Args , std :: enable_if_t <! negate_invocable_v < F , Args ... > , int > = 0 > void operator ()( Args && ...) && = delete ; template < class ... Args , std :: enable_if_t <! negate_invocable_v < const F , Args ... > , int > = 0 > void operator ()( Args && ...) const && = delete ; }; } template < class F > constexpr detail :: not_fn_t < std :: decay_t < F >> not_fn ( F && f ) { return { std :: forward < F > ( f )}; }
(2) not_fn
namespace detail { template < auto ConstFn > struct stateless_not_fn { template < class ... Args > constexpr auto operator ()( Args && ... args ) const noexcept ( noexcept ( ! std :: invoke ( ConstFn , std :: forward < Args > ( args )...))) -> decltype ( ! std :: invoke ( ConstFn , std :: forward < Args > ( args )...)) { return ! std :: invoke ( ConstFn , std :: forward < Args > ( args )...); } }; } template < auto ConstFn > constexpr detail :: stateless_not_fn < ConstFn > not_fn () noexcept { if constexpr ( std :: is_pointer_v < decltype ( ConstFn ) >

참고 사항

std::not_fn은 C++03 시대의 부정기(negator)인 std::not1std::not2를 대체하기 위한 것이에요.

Feature-test 매크로 표준 기능
__cpp_lib_not_fn 201603L (C++17) std::not_fn() , ( 1 )
202306L (C++26) Allow passing callable objects as non-type template arguments to std::not_fn , ( 2 )

예제

#include <cassert>
#include <functional>

bool is_same(int a, int b) noexcept
{
    return a == b;
}

struct S
{
    int val;
    bool is_same(int arg) const noexcept { return val == arg; }
};

int main()
{
    // Using with a free function:
    auto is_differ = std::not_fn(is_same);
    assert(is_differ(8, 8) == false); // equivalent to: !is_same(8, 8) == false
    assert(is_differ(6, 9) == true); // equivalent to: !is_same(8, 0) == true

    // Using with a member function:
    auto member_differ = std::not_fn(&S::is_same);
    assert(member_differ(S{3}, 3) == false); //: S tmp{6}; !tmp.is_same(6) == false

    // Noexcept-specification is preserved:
    static_assert(noexcept(is_differ) == noexcept(is_same));
    static_assert(noexcept(member_differ) == noexcept(&S::is_same));

    // Using with a function object:
    auto same = [](int a, int b) { return a == b; };
    auto differ = std::not_fn(same);
    assert(differ(1, 2) == true); //: !same(1, 2) == true
    assert(differ(2, 2) == false); //: !same(2, 2) == false

#if __cpp_lib_not_fn >= 202306L
    auto is_differ_cpp26 = std::not_fn<is_same>();
    assert(is_differ_cpp26(8, 8) == false);
    assert(is_differ_cpp26(6, 9) == true);

    auto member_differ_cpp26 = std::not_fn<&S::is_same>();
    assert(member_differ_cpp26(S{3}, 3) == false);

    auto differ_cpp26 = std::not_fn<same>();
    static_assert(differ_cpp26(1, 2) == true);
    static_assert(differ_cpp26(2, 2) == false);
#endif
}

같이 보기

not1 (C++17에서 deprecated) (C++20에서 제거) 사용자 정의 std::unary_negate 객체를 생성해요 (함수 템플릿) [edit]
not2 (C++17에서 deprecated) (C++20에서 제거) 사용자 정의 std::binary_negate 객체를 생성해요 (함수 템플릿) [edit]

더 알아보기 (Learn more)

cppreference