名稱空間
變體
操作

std::bind_front, std::bind_back

來自 cppreference.com
 
 
 
函式物件
部分函式應用
bind_frontbind_back
(C++20)(C++23)
(C++11)
函式呼叫
(C++17)(C++23)
恆等函式物件
(C++20)
透明運算子包裝器
(C++14)
(C++14)
(C++14)
(C++14)  
(C++14)
(C++14)
(C++14)
(C++14)
(C++14)
(C++14)
(C++14)
(C++14)
(C++14)

舊繫結器和介面卡
(直到 C++17*)
(直到 C++17*)
(直到 C++17*)
(直到 C++17*)  
(直到 C++17*)
(直到 C++17*)(直到 C++17*)(直到 C++17*)(直到 C++17*)
(直到 C++20*)
(直到 C++20*)
(直到 C++17*)(直到 C++17*)
(直到 C++17*)(直到 C++17*)

(直到 C++17*)
(直到 C++17*)(直到 C++17*)(直到 C++17*)(直到 C++17*)
(直到 C++20*)
(直到 C++20*)
 
定義於標頭檔案 <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_frontstd::bind_back 生成一個完美轉發的呼叫包裝器,允許用其 (1,2)sizeof...(Args) 個引數或 (3,4)sizeof...(Args) 個引數繫結到 args 來呼叫可呼叫目標。

1,3) 呼叫包裝器持有目標可呼叫物件 f 的一個副本。
2,4) 呼叫包裝器不持有可呼叫目標(它在靜態上確定)。
1) std::bind_front(f, bound_args...)(call_args...) 等價於
std::invoke(f, bound_args..., call_args...)
2) std::bind_front<ConstFn>(bound_args...)(call_args...) 等價於
std::invoke(ConstFn, bound_args..., call_args...)
3) std::bind_back(f, bound_args...)(call_args...) 等價於
std::invoke(f, call_args..., bound_args...)
4) std::bind_back<ConstFn>(bound_args...)(call_args...) 等價於
std::invoke(ConstFn, call_args..., bound_args...)

以下條件必須為 true,否則程式是非良構的

目錄

[edit] 引數

f - 可呼叫 物件(函式物件、函式指標、函式引用、成員函式指標或資料成員指標),它將被繫結到某些引數
args - 要繫結到可呼叫目標 (1,2) 前或 (3,4)sizeof...(Args) 個引數的引數列表
型別要求
-
std::decay_t<F> 必須滿足 可移動構造 的要求。
-
std::decay_t<Args>... 必須滿足 可移動構造 的要求。
-
decltype(ConstFn) 必須滿足 可呼叫 的要求。

[edit] 返回值

一個型別為 T 的函式物件(呼叫包裝器),其型別未指定,但兩次以相同引數呼叫 std::bind_frontstd::bind_back 返回的物件的型別相同。

bind-partialstd::bind_frontstd::bind_back

返回的物件具有以下屬性

bind-partial 返回型別

成員物件

返回的物件表現得像它持有

1,3) 一個型別為 std::decay_t<F> 的成員物件 fd,直接非列表初始化自 std::forward<F>(f),以及
1-4) 一個 std::tuple 物件 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...) 中被呼叫時,儲存物件的呼叫發生,如同透過

1) std::invoke(g.fd, std::get<Ns>(g.tup)..., call_args...),當 bind-partialstd::bind_front 時,
2) std::invoke(ConstFn, std::get<Ns>(g.tup)..., call_args...),當 bind-partialstd::bind_front 時,
3) std::invoke(g.fd, call_args..., std::get<Ns>(g.tup)...),當 bind-partialstd::bind_back 時,
4) std::invoke(ConstFn, call_args..., std::get<Ns>(g.tup)...),當 bind-partialstd::bind_back 時,

其中

  • 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] 異常

1,3) 丟擲儲存函式物件的建構函式丟擲的任何異常。
1-4) 丟擲任何繫結引數的建構函式丟擲的任何異常。

[edit] 備註

這些函式模板旨在替代 std::bind。與 std::bind 不同,它們不支援任意引數重排,並且對巢狀繫結表示式或 std::reference_wrapper 沒有特殊處理。另一方面,它們關注呼叫包裝器物件的價值類別,並傳播底層呼叫運算子的異常規範。

std::invoke 中所述,當呼叫指向非靜態成員函式或指向非靜態資料成員的指標時,第一個引數必須是引用或指標(包括可能的智慧指標,如 std::shared_ptrstd::unique_ptr)到將訪問其成員的物件。

std::bind_frontstd::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)

[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)
將一個或多個引數繫結到函式物件
(函式模板) [edit]
(C++11)
從指向成員的指標建立函式物件
(函式模板) [edit]