functional_bind_front

functional_bind_front (한국어 부제)

std::bind_frontstd::bind_back는 호출 가능 대상의 앞쪽 또는 뒤쪽 인자를 고정한 완벽 전달 호출 래퍼를 생성하는 함수 템플릿이에요. C++20에서 std::bind_front가, C++23에서 std::bind_back이 도입되었고, C++26에서는 호출 가능 객체를 비타입 템플릿 인자로 받는 오버로드가 추가되었어요. 이 페이지에서는 두 함수 템플릿의 동작 방식과 제약 조건, 사용 예시를 살펴봐요.

출처: cppreference

본문

정의

<functional> 헤더에 정의됨
std::bind_front
template < class F , class ... Args > constexpr /* unspecified */ bind_front ( F && f , Args && ... args ); (1) (since C++20)
template < auto ConstFn , class ... Args > constexpr /* unspecified */ bind_front ( Args && ... args ); (2) (since C++26)
std::bind_back
template < class F , class ... Args > constexpr /* unspecified */ bind_back ( F && f , Args && ... args ); (3) (since C++23)
template < auto ConstFn , class ... Args > constexpr /* unspecified */ bind_back ( Args && ... args ); (4) (since C++26)

함수 템플릿 std::bind_frontstd::bind_back는 완벽 전달 호출 래퍼를 생성해요. 이 래퍼는 호출 가능 대상을 호출할 때 (1,2) 첫 번째 또는 (3,4) 마지막 sizeof...(Args) 개의 매개변수를 args에 바인딩해요.

다음 조건이 충족되어야 하며, 그렇지 않으면 프로그램은 ill-formed예요.

  • (1,3) std::is_constructible_v<std::decay_t<F>, F>
  • (1,3) std::is_move_constructible_v<std::decay_t<F>>
  • (2,4) decltype(ConstFn)이 포인터 또는 멤버 포인터라면 ConstFn은 null 포인터가 아니어야 해요.
  • (std::is_constructible_v<std::decay_t<Args>, Args> && ...)
  • (std::is_move_constructible_v<std::decay_t<Args>> && ...)

매개변수

매개변수 설명
f 일부 인자에 바인딩될 호출 가능 객체(함수 객체, 함수 포인터, 함수 참조, 멤버 함수 포인터, 데이터 멤버 포인터)
args 호출 가능 대상의 (1,2) 첫 번째 또는 (3,4) 마지막 sizeof...(Args) 개의 매개변수에 바인딩할 인자 목록

타입 요구 사항

  • std::decay_t<F>MoveConstructible 요구 사항을 충족해야 해요.
  • std::decay_t<Args>...MoveConstructible 요구 사항을 충족해야 해요.
  • decltype(ConstFn)Callable 요구 사항을 충족해야 해요.

반환값

타입 T의 함수 객체(호출 래퍼)를 반환해요. T는 지정되지 않지만, 같은 인자로 std::bind_front 또는 std::bind_back를 두 번 호출해 얻은 객체의 타입은 같아요.

bind-partialstd::bind_front 또는 std::bind_back 중 하나를 의미해요.

반환된 객체는 다음과 같은 속성을 가져요.

bind-partial 반환 타입

멤버 객체

반환된 객체는 다음과 같은 멤버 객체를 보유한 것처럼 동작해요.

생성자

bind-partial의 반환 타입은 복사/이동 생성자가 멤버별 복사/이동을 수행하는 것처럼 동작해요. 모든 멤버 객체가 CopyConstructible이면 CopyConstructible이고, 그렇지 않으면 MoveConstructible이에요.

멤버 함수 operator()

이전에 (1,3) bind-partial(f, args...) 또는 (2,4) bind-partial<ConstFn>(args...) 호출로 얻은 객체 G가 있다고 할 때, gG를 가리키는 glvalue이고 함수 호출 표현식 g(call_args...)에서 호출된다면, 저장된 객체의 호출이 다음과 같이 수행돼요.

여기서

  • Ns는 정수 팩 0, 1, ..., (sizeof...(Args) - 1)이고, g가 호출 표현식에서 lvalue라면 std::invoke 표현식에서도 lvalue이며, 그렇지 않으면 rvalue예요. 따라서 std::move(g)(call_args...)는 바인딩된 인자를 호출로 이동할 수 있고, g(call_args...)는 복사해요.
  • g가 volatile 한정 타입이면 프로그램은 ill-formed예요.

멤버 operator()는 호출하는 std::invoke 표현식이 noexcept라면 noexcept예요. 즉, 기본 호출 연산자의 예외 명세를 그대로 유지해요.

Exceptions

Notes

이 함수 템플릿들은 std::bind를 대체하기 위한 것이에요. std::bind와 달리 임의의 인자 재배열을 지원하지 않고, 중첩된 bind 표현식이나 std::reference_wrapper에 대한 특별한 처리가 없어요. 반면에 호출 래퍼 객체의 값 범주에 주의하고 기본 호출 연산자의 예외 명세를 전파해요.

std::invoke에 설명된 대로, 비정적 멤버 함수 포인터나 비정적 데이터 멤버 포인터를 호출할 때 첫 번째 인자는 멤버에 접근할 객체에 대한 참조 또는 포인터(스마트 포인터 std::shared_ptr, std::unique_ptr 포함)여야 해요.

std::bind_front 또는 std::bind_back의 인자는 복사되거나 이동되며, std::refstd::cref로 감싸지 않는 한 참조로 전달되지 않아요.

일반적으로 (1) std::bind_front와 (3) std::bind_back을 사용해 함수나 멤버 함수에 인자를 바인딩하려면 함수 포인터와 인자를 저장해야 해요. 언어가 정확히 어떤 함수를 호출할지 알고 있어도 포인터 역참조가 필요하지 않기 때문이에요. 이러한 경우 "제로 비용"을 보장하기 위해 C++26에서는 호출 가능 객체를 비타입 템플릿 인자로 받는 (2,4) 버전을 도입했어요.

기능 테스트 매크로 표준 기능
__cpp_lib_bind_front 201907L (C++20) std::bind_front, (1)
202306L (C++26) 호출 가능 객체를 비타입 템플릿 인자로 std::bind_front에 전달 허용, (2)
__cpp_lib_bind_back 202202L (C++23) std::bind_back, (3)
202306L (C++26) 호출 가능 객체를 비타입 템플릿 인자로 std::bind_back에 전달 허용, (4)

가능한 구현

(2) bind_front
namespace detail { template < typename To , typename From > using forward_like_t = decltype ( std :: forward_like < From > ( std :: declval < To > ())); } template < auto ConstFn , class ... Args > constexpr auto bind_front ( Args && ... args ) { using F = decltype ( ConstFn ); if constexpr ( std :: is_pointer_v < F > or std :: is_member_pointer_v < F > ) static_assert ( ConstFn != nullptr ); return [... bound_args ( std :: forward < Args > ( args ))] < class Self , class ... T > ( this Self && , T && ... call_args ) noexcept ( std :: is_nothrow_invocable_v < F , detail :: forward_like_t < std :: decay_t < Args > , Self > ..., T ... > ) -> std :: invoke_result_t < F , detail :: forward_like_t < std :: decay_t < Args > , Self > ..., T ... > { return std :: invoke ( ConstFn , std :: forward_like < Self > ( bound_args )..., std :: forward < T > ( call_args )... ); }; }
(4) bind_back
namespace detail { /* is the same as above */ } template < auto ConstFn , class ... Args > constexpr auto bind_back ( Args && ... args ) { using F = decltype ( ConstFn ); if constexpr ( std :: is_pointer_v < F > or std :: is_member_pointer_v < F > ) static_assert ( ConstFn != nullptr ); return [... bound_args ( std :: forward < Args > ( args ))] < class Self , class ... T > ( this Self && , T && ... call_args ) noexcept ( std :: is_nothrow_invocable_v < F , T ..., detail :: forward_like_t < std :: decay_t < Args > , Self > ... > ) -> std :: invoke_result_t < F , T ..., detail :: forward_like_t < std :: decay_t < Args > , Self > ... > { return std :: invoke ( ConstFn , std :: forward < T > ( call_args )..., std :: forward_like < Self > ( bound_args )... ); }; }

예제

#include <cassert>
#include <functional>

int minus(int a, int b)
{
    return a - b;
}

struct S
{
    int val;
    int minus(int arg) const noexcept { return val - arg; }
};

int main()
{
    auto fifty_minus = std::bind_front(minus, 50);
    assert(fifty_minus(3) == 47); // equivalent to: minus(50, 3) == 47

    auto member_minus = std::bind_front(&S::minus, S{50});
    assert(member_minus(3) == 47); //: S tmp{50}; tmp.minus(3) == 47

    // Noexcept-specification is preserved:
    static_assert(!noexcept(fifty_minus(3)));
    static_assert(noexcept(member_minus(3)));

    // Binding of a lambda:
    auto plus = [](int a, int b) { return a + b; };
    auto forty_plus = std::bind_front(plus, 40);
    assert(forty_plus(7) == 47); // equivalent to: plus(40, 7) == 47

#if __cpp_lib_bind_front >= 202306L
    auto fifty_minus_cpp26 = std::bind_front<minus>(50);
    assert(fifty_minus_cpp26(3) == 47);

    auto member_minus_cpp26 = std::bind_front<&S::minus>(S{50});
    assert(member_minus_cpp26(3) == 47);

    auto forty_plus_cpp26 = std::bind_front<plus>(40);
    assert(forty_plus(7) == 47);
#endif

#if __cpp_lib_bind_back >= 202202L
    auto madd = [](int a, int b, int c) { return a * b + c; };
    auto mul_plus_seven = std::bind_back(madd, 7);
    assert(mul_plus_seven(4, 10) == 47); //: madd(4, 10, 7) == 47
#endif

#if __cpp_lib_bind_back >= 202306L
    auto mul_plus_seven_cpp26 = std::bind_back<madd>(7);
    assert(mul_plus_seven_cpp26(4, 10) == 47);
#endif
}

참고 문헌

  • C++26 표준 (ISO/IEC 14882:2026):
    • TBD Function templates bind_front and bind_back [func.bind.partial]
  • C++23 표준 (ISO/IEC 14882:2024):
    • 22.10.14 Function templates bind_front and bind_back [func.bind.partial]
  • C++20 표준 (ISO/IEC 14882:2020):
    • 20.14.14 Function template bind_front [func.bind.front]

같이 보기

bind (C++11) 하나 이상의 인자를 함수 객체에 바인딩해요 (함수 템플릿)
mem_fn (C++11) 멤버 포인터로부터 함수 객체를 만들어요 (함수 템플릿)

더 알아보기 (Learn more)

cppreference