std::bind

来自cppreference.com
< cpp‎ | utility‎ | functional
 
 
工具库
通用工具
日期和时间
函数对象
格式化库 (C++20)
(C++11)
关系运算符 (C++20 中弃用)
整数比较函数
(C++20)(C++20)(C++20)
(C++20)
swap 与类型运算
(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)
 
函数对象
函数包装
(C++11)
(C++11)
部分函数应用
bind
(C++11)
(C++20)(C++23)
函数调用
(C++17)(C++23)
恒等函数对象
(C++20)
引用包装
(C++11)(C++11)
通透运算符包装
(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> 定义
(1)
template< class F, class... Args >
/* 未指定 */ bind( F&& f, Args&&... args );
(C++11 起)
(C++20 前)
template< class F, class... Args >
constexpr /* 未指定 */ bind( F&& f, Args&&... args );
(C++20 起)
(2)
template< class R, class F, class... Args >
/* 未指定 */ bind( F&& f, Args&&... args );
(C++11 起)
(C++20 前)
template< class R, class F, class... Args >
constexpr /* 未指定 */ bind( F&& f, Args&&... args );
(C++20 起)

函数模板 std::bind 生成 f 的转发调用包装器。调用此包装器等价于以一些绑定args 的参数调用 f

如果 std::is_constructible<std::decay<F>::type, F>::valuefalse,或 std::is_constructible<std::decay<Arg_i>::type, Arg_i>::value 对于 Args 中的某个类型 Arg_ifalse,那么程序非良构。

如果 std::decay<Ti>::typeArgs 中的某个类型不可移动构造 (MoveConstructible) 可析构 (Destructible) ,那么行为未定义。

参数

f - 可调用 (Callable) 对象(函数对象、指向函数指针、到函数引用、指向成员函数指针或指向数据成员指针)
args - 要绑定的参数列表,未绑定参数会被命名空间 std::placeholders占位符 _1, _2, _3... 替换

返回值

某个未指定类型 T 的函数对象 g,其中 std::is_bind_expression<T>::valuetrue。它有下列属性:

std::bind 返回类型

成员对象

std::bind 的返回类型保有从 std::forward<F>(f) 构造的 std::decay<F>::type 类型成员对象,和对于 args... 中每个参数的类型是 std::decay<Arg_i>::type 的各一个对象,类似地从 std::forward<Arg_i>(arg_i) 构造。

构造函数

如果 std::bind 的返回类型的所有成员类型(说明如上)都可复制构造 (CopyConstructible) 那么它可复制构造 (CopyConstructible) ,否则它可移动构造 (MoveConstructible) 。类型定义下列成员:

成员类型 result_type

1) (C++17 中弃用) 如果 F 是指向函数或指向成员函数的指针,那么 result_typeF 的返回类型。如果 F 是有嵌套 typedef result_type 的类类型,那么 result_typeF::result_type。否则不定义 result_type
2) (C++17 中弃用) result_typeR
(C++20 前)

成员函数 operator()

从函数调用表达式 g(u1, u2, ... uM) 调用 g 时,发生被存储对象的调用,如同以

1) INVOKE(fd, std::forward<V1>(v1), std::forward<V2>(v2), ..., std::forward<VN>(vN))
2) INVOKE<R>(fd, std::forward<V1>(v1), std::forward<V2>(v2), ..., std::forward<VN>(vN))

,其中 fdstd::decay<F>::type 类型的值,受绑定参数 v1, v2, ..., vN 参数的值和类型按以下方式确定。

如果在到 g() 调用的中提供一些参数与 g 存储的任何占位符都不匹配,那么求值并忽略未使用的参数。

operator() 调用当且仅当底层 INVOKE 操作不抛出或是常量子表达式 (C++20 起)时也具有这些属性。operator() 只有在 INVOKE 操作在作为不求值操作数良构时才会参与重载决议。

如果 g 具有 volatile 限定,那么程序非良构。

如果 INVOKE(fd, w1, w2, ..., wN) 对于任何值 w1, w2, ..., wN 都不合法,那么行为未定义。

受绑定参数

对于每个存储的参数 arg_iINVOKEINVOKE<R> 操作中的受绑定参数 v_i 通过以下方式确定:

第1种情况:引用包装

如果 arg_i 拥有类型 std::reference_wrapper<T>(例如,在起始的到 std::bind 调用中使用了 std::refstd::cref),那么 v_iarg_i.get() 且它的类型 V_iT&:存储的参数按引用传递进入被调用的函数对象。

第2种情况:绑定表达式

如果 arg_i 拥有类型 T 并且 std::is_bind_expression<T>::valuetrue(例如,直接传递到起始的对 std::bind 调用的另一 std::bind 表达式),那么 std::bind 进行函数组合:不是传递该绑定子表达式将返回的函数对象,而是立即求值该子表达式,并将它的值传递给外层可调用对象。如果 bind 子表达式拥有任何占位符参数,那么将它们与外层 bind 共享(从 u1, u2, ... 中选出)。特别是,v_iarg_i(std::forward<Uj>(uj)...) 而它的类型 V_istd::result_of<T cv &(Uj&&...)>::type&& (C++17 前)std::invoke_result_t<T cv &, Uj&&...>&& (C++17 起)(cv 限定与 g 的相同)。

第3种情况:占位符

如果 arg_i 拥有类型 T 并且 std::is_placeholder<T>::value 不是 0(表示以如 std::placeholders::_1, _2, _3, ... 的占位符为到 std::bind 初始调用的参数),那么将占位符所指示的参数(_1u1_2u2 等)传递给可调用对象:v_istd::forward<Uj>(uj) 而它的类型 V_iUj&&

第4种情况:通常参数

否则,arg_i 会作为左值参数传递给可调用对象:v_i 单纯地是 arg_i 而它的类型 V_iT cv &,其中 cv 是与 g 相同的 cv 限定。

异常

只有在从 std::forward<F>(f) 构造 std::decay<F>::type 抛出,或从 std::forward<Arg_i>(arg_i) 构造对应的任何 std::decay<Arg_i>::type 抛出的情况下才会抛出异常,其中 Arg_iArgs... args 中的第 i 个类型,而 arg_i 是第 i 个参数。

注解

可调用 (Callable) 中描述,调用指向非静态成员函数指针或指向非静态数据成员指针时,首参数必须是引用或指针(可以包含智能指针,如 std::shared_ptrstd::unique_ptr),指向将访问它的成员的对象。

std::bind 的参数被复制或移动,而且决不按引用传递,除非以 std::refstd::cref 包装。

允许同一 bind 表达式中的多重占位符(例如多个 _1),但结果只有在对应参数(u1)是左值或不可移动右值时才有良好定义。

示例

#include <functional>
#include <iostream>
#include <memory>
#include <random>
 
void f(int n1, int n2, int n3, const int& n4, int n5)
{
    std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
}
 
int g(int n1)
{
    return n1;
}
 
struct Foo
{
    void print_sum(int n1, int n2)
    {
        std::cout << n1 + n2 << '\n';
    }
 
    int data = 10;
};
 
int main()
{
    using namespace std::placeholders;  // 对于 _1, _2, _3...
 
    std::cout << "1) 参数重排序和按引用传递:";
    int n = 7;
    // ( _1 与 _2 来自 std::placeholders ,并表示将来会传递给 f1 的参数)
    auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n);
    n = 10;
    f1(1, 2, 1001); // 1 为 _1 所绑定, 2 为 _2 所绑定,不使用 1001
                    // 进行到 f(2, 42, 1, n, 7) 的调用
 
    std::cout << "2) 使用 lambda 达成相同效果:";
    n = 7;
    auto lambda = [&ncref = n, n](auto a, auto b, auto /* 未使用 */)
    {
        f(b, 42, a, ncref, n);
    };
    n = 10;
    lambda(1, 2, 1001); // 等同于调用 f1(1, 2, 1001)
 
    std::cout << "3) 嵌套 bind 子表达式共享占位符:";
    auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);
    f2(10, 11, 12); // 进行到 f(12, g(12), 12, 4, 5); 的调用
 
    std::cout << "4) 以分布绑定随机数生成器:";
    std::default_random_engine e;
    std::uniform_int_distribution<> d(0, 10);
    std::function<int()> rnd = std::bind(d, e); // e 的一个副本存储于 rnd
    for (int n = 0; n < 10; ++n)
        std::cout << rnd() << ' ';
    std::cout << '\n';
 
    std::cout << "5) 绑定指向成员函数指针:";
    Foo foo;
    auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);
    f3(5);
 
    std::cout << "6) 绑定是指向成员函数指针的 mem_fn:";
    auto ptr_to_print_sum = std::mem_fn(&Foo::print_sum);
    auto f4 = std::bind(ptr_to_print_sum, &foo, 95, _1);
    f4(5);
 
    std::cout << "7) 绑定指向数据成员指针:";
    auto f5 = std::bind(&Foo::data, _1);
    std::cout << f5(foo) << '\n';
 
    std::cout << "8) 绑定是指向数据成员指针的 mem_fn:";
    auto ptr_to_data = std::mem_fn(&Foo::data);
    auto f6 = std::bind(ptr_to_data, _1);
    std::cout << f6(foo) << '\n';
 
    std::cout << "9) 使用智能指针调用被引用对象的成员:";
    std::cout << f6(std::make_shared<Foo>(foo)) << '\n'
              << f6(std::make_unique<Foo>(foo)) << '\n';
}

输出:

1) 参数重排序和按引用传递:2 42 1 10 7
2) 使用 lambda 达成相同效果:2 42 1 10 7
3) 嵌套 bind 子表达式共享占位符:12 12 12 4 5
4) 以分布绑定随机数生成器:0 1 8 5 5 2 0 7 7 10 
5) 绑定指向成员函数指针:100
6) 绑定是指向成员函数指针的 mem_fn:100
7) 绑定指向数据成员指针:10
8) 绑定是指向数据成员指针的 mem_fn:10
9) 使用智能指针调用被引用对象的成员:10 10

缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

缺陷报告 应用于 出版时的行为 正确行为
LWG 2021 C++11 1. 绑定参数没有转发到 fd
2. 在第2种情况下,V_i
    std::result_of<T cv (Uj...)>::type
1. 转发绑定参数
2. 更改成
    std::result_of<T cv &(Uj&&...)>::type&&

参阅

按顺序绑定一定数量的参数到函数对象
(函数模板)
用作 std::bind 表达式中的未绑定实参的占位符
(常量)
(C++11)
从成员指针创建出函数对象
(函数模板)