命名空间
变体
操作

std::bind_front, std::bind_back

来自 cppreference.com
< cpp‎ | utility‎ | functional
 
 
实用程序库
语言支持
类型支持 (基本类型,RTTI)
库功能测试宏 (C++20)
动态内存管理
程序实用程序
协程支持 (C++20)
可变参数函数
调试支持
(C++26)
三方比较
(C++20)
(C++20)(C++20)(C++20)
(C++20)(C++20)(C++20)
通用实用程序
日期和时间
函数对象
格式化库 (C++20)
(C++11)
关系运算符 (C++20 中已弃用)
整数比较函数
(C++20)(C++20)(C++20)   
(C++20)
交换类型操作
(C++14)
(C++11)
(C++11)
(C++11)
(C++17)
通用词汇类型
(C++11)
(C++17)
(C++17)
(C++17)
(C++11)
(C++17)
(C++23)
基本字符串转换
(C++17)
(C++17)

 
函数对象
部分函数应用
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 /* 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_frontstd::bind_back 生成一个完美转发调用包装器,它允许使用其 (1,2) 第一个或 (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 - Callable 对象(函数对象、指向函数的指针、指向函数的引用、指向成员函数的指针或指向数据成员的指针),将被绑定到一些参数。
args - 要绑定到 (1,2) 首个或 (3,4) 最终 sizeof...(Args) 可调用目标参数的参数列表。
类型要求
-
std::decay_t<F> 必须满足 MoveConstructible 的要求。
-
std::decay_t<Args>... 必须满足 MoveConstructible 的要求。
-
decltype(ConstFn) 必须满足 Callable 的要求。

[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<std::decay_t<Args>...>(std::forward<Args>(args)...) 构造的 std::tuple 对象 tup,除了返回对象的赋值行为是未指定的,并且名称仅用于说明。

构造函数

bind-partial 的返回值类型表现得好像其复制/移动构造函数执行成员逐个复制/移动。如果其所有成员对象(如上所述)都是 CopyConstructible,则它是 CopyConstructible,否则它是 MoveConstructible

成员函数 operator()

给定一个从之前对 (1,3) bind-partial(f, args...)(2,4) bind-partial<ConstFn>(args...) 的调用中获得的对象 G,当指定 G 的 glvalue 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)
  • gstd::invoke 表达式中的 lvalue,如果它是调用表达式中的 lvalue,否则它是 rvalue。因此 std::move(g)(call_args...) 可以将绑定参数移动到调用中,而 g(call_args...) 将进行复制。

如果 g 的类型为限定为 volatile 的类型,则程序将是非法的。

成员 operator()noexcept,如果它调用的 std::invoke 表达式是 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)

[编辑] 可能的实现

(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)...);
        };
}

[编辑] 示例

#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 函数模板 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)
将一个或多个参数绑定到函数对象
(函数模板) [编辑]
(C++11)
从指向成员的指针创建一个函数对象
(函数模板) [编辑]