std::bind_front, std::bind_back
定義於標頭檔案 <functional> |
||
std::bind_front |
||
template< class F, class... Args > constexpr /* 未指定 */ bind_front( F&& f, Args&&... args ); |
(1) | (C++20 起) |
template< auto ConstFn, class... Args > constexpr /* 未指定 */ bind_front( Args&&... args ); |
(2) | (C++26 起) |
std::bind_back |
||
template< class F, class... Args > constexpr /* 未指定 */ bind_back( F&& f, Args&&... args ); |
(3) | (C++23 起) |
template< auto ConstFn, class... Args > constexpr /* 未指定 */ bind_back( Args&&... args ); |
(4) | (C++26 起) |
函式模板 std::bind_front
和 std::bind_back
生成一個完美轉發的呼叫包裝器,允許用其 (1,2) 前 sizeof...(Args) 個引數或 (3,4) 後 sizeof...(Args) 個引數繫結到 args
來呼叫可呼叫目標。
f
的一個副本。以下條件必須為 true,否則程式是非良構的
- (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>> && ...).
目錄 |
[edit] 引數
f | - | 可呼叫 物件(函式物件、函式指標、函式引用、成員函式指標或資料成員指標),它將被繫結到某些引數 |
args | - | 要繫結到可呼叫目標 (1,2) 前或 (3,4) 後 sizeof...(Args) 個引數的引數列表 |
型別要求 | ||
-std::decay_t<F> 必須滿足 可移動構造 的要求。 | ||
-std::decay_t<Args>... 必須滿足 可移動構造 的要求。 | ||
-decltype(ConstFn) 必須滿足 可呼叫 的要求。 |
[edit] 返回值
一個型別為 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
的返回型別表現得像其複製/移動建構函式執行成員級複製/移動。如果其所有成員物件(如上所述)是 可複製構造 的,則它是 可複製構造 的;否則它是 可移動構造 的。
成員函式 operator()
給定一個物件 G
,它是由早先呼叫 (1,3) bind-partial(f, args...)
或 (2,4) bind-partial<ConstFn>(args...)
獲得的,當一個指定 G
的左值 g
在函式呼叫表示式 g(call_args...) 中被呼叫時,儲存物件的呼叫發生,如同透過
其中
-
Ns
是一個整數包0, 1, ..., (sizeof...(Args) - 1)
, - 如果
g
在呼叫表示式中是左值,則在 std::invoke 表示式中g
是左值,否則是右值。因此 std::move(g)(call_args...) 可以將繫結的引數移動到呼叫中,而 g(call_args...) 將會複製。
-
如果 g
具有 volatile 限定型別,則程式是非良構的。
如果其呼叫的 std::invoke 表示式是 noexcept,則成員 operator() 是 noexcept(換句話說,它保留了底層呼叫運算子的異常規範)。
[edit] 異常
[edit] 備註
這些函式模板旨在替代 std::bind。與 std::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) |
[edit] 可能的實現
(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 { /* 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, 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)...); }; } |
[edit] 示例
#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 }
[edit] 參考文獻
- C++26 標準 (ISO/IEC 14882:2026)
- 待定 函式模板 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]
[edit] 參閱
(C++11) |
將一個或多個引數繫結到函式物件 (函式模板) |
(C++11) |
從指向成員的指標建立函式物件 (函式模板) |