functional_bind_front
functional_bind_front (한국어 부제)
std::bind_front와 std::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_front와 std::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-partial은 std::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가 있다고 할 때, g가 G를 가리키는 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::ref나 std::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) |
멤버 포인터로부터 함수 객체를 만들어요 (함수 템플릿) |