std::bind_front, std::bind_back
|
헤더 <functional>
|
||
std::bind_front |
||
template< class F, class... Args >
constexpr /* unspecified */ bind_front( F&& f, Args&&... args );
|
에 정의됨 (1) | (C++20 이후) |
template< auto ConstFn, class... Args >
constexpr /* unspecified */ bind_front( Args&&... args );
|
(2) | (C++26 이후) |
std::bind_back |
||
template< class F, class... Args >
constexpr /* unspecified */ bind_back( F&& f, Args&&... args );
|
(3) | (C++23 이후) |
template< auto ConstFn, class... Args >
constexpr /* unspecified */ bind_back( Args&&... args );
|
(4) | (C++26 이후) |
함수 템플릿 std::bind_front과 std::bind_back는 완벽 전달 호출 래퍼를 생성합니다. 이 래퍼는 호출 가능 대상을 호출할 때 (1,2) 처음 개 또는 (3,4)sizeof...(Args) 마지막 args 개의 매개변수를
f의 복사본을 보유합니다.std::invoke(f, bound_args..., call_args...).std::invoke(ConstFn, bound_args..., call_args...).std::invoke(f, call_args..., bound_args...).std::invoke(ConstFn, call_args..., bound_args...).
다음 조건이 충족되어야 true하며, 그렇지 않으면 프로그램의 형식이 잘못되었습니다(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는 널 포인터가 아닙니다, (std::is_constructible_v<std::decay_t<Args>, Args> && ...),(std::is_move_constructible_v<std::decay_t<Args>> && ...).
매개변수
| f | - | Callable 객체(함수 객체, 함수 포인터, 함수 참조, 멤버 함수 포인터, 또는 데이터 멤버 포인터)로, 일부 인수에 바인딩될 대상 |
| args | - | 호출 가능 대상의 ( 1,2 ) 처음 또는 ( 3,4 ) 마지막 sizeof... ( Args ) 개 매개변수에 바인딩할 인수 목록 |
| 타입 요구사항 | ||
-
std::
decay_t
<
F
>
는
MoveConstructible
요구사항을 충족해야 함
|
||
-
std::
decay_t
<
Args
>
...
는
MoveConstructible
요구사항을 충족해야 함
|
||
-
decltype
(
ConstFn
)
는
Callable
요구사항을 충족해야 함
|
||
반환값
함수 객체(호출 래퍼)의 타입은
T
로 지정되지 않으며, 동일한 인자들로
std::bind_front
또는
std::bind_back
를 두 번 호출하여 반환된 객체들의 타입이 동일하다는 점만 예외입니다.
bind-partial
을
std::bind_front
또는
std::bind_back
중 하나로 설정합니다.
반환된 객체는 다음과 같은 속성을 가집니다:
bind-partial 반환 타입
멤버 객체
반환된 객체는 다음과 같이 보유하는 것처럼 동작합니다:
tup
가
std::
tuple
<
std::
decay_t
<
Args
>
...
>
(
std::
forward
<
Args
>
(
args
)
...
)
로 생성되지만, 반환된 객체의 할당 동작은 명시되지 않았으며 이름들은 설명 목적으로만 사용됩니다.
생성자
bind-partial
의 반환 타입은 복사/이동 생성자가 멤버별 복사/이동을 수행하는 것처럼 동작합니다. 이는 모든 멤버 객체(위에서 명시된)가
CopyConstructible
일 경우
CopyConstructible
이며, 그렇지 않을 경우
MoveConstructible
입니다.
멤버 함수
operator()
이전 호출에서 얻은 객체
G
가
(
1,3
)
bind-partial
(f, args...)
또는
(
2,4
)
bind-partial
<ConstFn>(args...)
로부터 얻어졌을 때, glvalue
g
가
G
를 지정하는 경우 함수 호출 표현식
g
(
call_args...
)
에서 호출되면, 저장된 객체의 호출이 다음과 같이 수행됩니다:
bind-partial
이
std::bind_front
인 경우,
bind-partial
가
std::bind_front
인 경우
bind-partial
이
std::bind_back
인 경우,
bind-partial
가
std::bind_back
일 때
여기서
-
-
Ns는 정수 팩0, 1, ..., (sizeof...(Args) - 1)입니다, -
g는 호출 표현식에서 lvalue인 경우 std::invoke 표현식에서 lvalue이며, 그렇지 않은 경우 rvalue입니다. 따라서 std :: move ( g ) ( call_args... ) 는 바인딩된 인자들을 호출로 이동시킬 수 있지만, g ( call_args... ) 는 복사합니다.
-
프로그램은
g
가 volatile 한정 타입을 가질 경우 형식이 잘못되었습니다.
멤버 operator ( ) 는 noexcept 입니다. 만약 std::invoke 표현식이 noexcept인 경우 (다시 말해, 기본 호출 연산자의 예외 명세를 보존합니다).
예외
참고 사항
이 함수 템플릿들은
std::bind
를 대체하기 위해 고안되었습니다.
std::bind
와 달리, 이들은 임의의 인자 재배열을 지원하지 않으며 중첩된 bind-expression이나
std::reference_wrapper
에 대한 특별한 처리를 제공하지 않습니다. 반면에, 이들은 호출 래퍼 객체의 값 범주(value category)를 고려하며 기본 호출 연산자의 예외 명세(exception specification)를 전파합니다.
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<class T, class U> struct copy_const : std::conditional<std::is_const_v<T>, U const, U> {}; template<class T, class U, class X = typename copy_const<std::remove_reference_t<T>, U>::type> struct copy_value_category : std::conditional<std::is_lvalue_reference_v<T&&>, X&, X&&> {}; template <class T, class U> struct type_forward_like : copy_value_category<T, std::remove_reference_t<U>> {}; template <class T, class U> using type_forward_like_t = typename type_forward_like<T, U>::type; } 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::type_forward_like_t<Self, std::decay_t<Args>>..., T...> ) -> std::invoke_result_t<F, detail::type_forward_like_t<Self, std::decay_t<Args>>..., T...> { return std::invoke(ConstFn, std::forward_like<Self>(bound_args)..., std::forward<T>(call_args)...); }; } |
| (4) bind_back |
namespace detail { /* 위와 동일 */ } 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, detail::type_forward_like_t<Self, T..., std::decay_t<Args>>...> ) -> std::invoke_result_t<F, detail::type_forward_like_t<Self, T..., std::decay_t<Args>>...> { 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); // 동일: 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 명세가 보존됨: static_assert(!noexcept(fifty_minus(3))); static_assert(noexcept(member_minus(3))); // 람다 바인딩: auto plus = [](int a, int b) { return a + b; }; auto forty_plus = std::bind_front(plus, 40); assert(forty_plus(7) == 47); // 동일: 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 함수 템플릿 bind_front 및 bind_back [func.bind.partial]
- C++23 표준 (ISO/IEC 14882:2024):
-
- 22.10.14 함수 템플릿 bind_front 및 bind_back [func.bind.partial]
- C++20 표준 (ISO/IEC 14882:2020):
-
- 20.14.14 함수 템플릿 bind_front [func.bind.front]
참고 항목
|
(C++11)
|
하나 이상의 인수를 함수 객체에 바인딩합니다
(function template) |
|
(C++11)
|
멤버 포인터로부터 함수 객체를 생성합니다
(function template) |