命名空间
变体
操作

std::bind

来自 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)

 
函数对象
函数调用
(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> 中定义
template< class F, class... Args >
/* 未指定 */ bind( F&& f, Args&&... args );
(1) (自 C++11)
(自 C++20 起为 constexpr)
template< class R, class F, class... Args >
/* 未指定 */ bind( F&& f, Args&&... args );
(2) (自 C++11)
(自 C++20 起为 constexpr)

函数模板 std::bindf 生成一个转发调用包装器。调用此包装器等效于调用 f,其中部分参数 绑定args

如果 std::is_constructible<std::decay<F>::type, F>::valuefalse,或者 std::is_constructible<std::decay<Arg_i>::type, Arg_i>::valuefalse,对于 Args 中的任何类型 Arg_i,程序均为非法的。

如果 std::decay<Ti>::typeArgs 中的任何类型不是 MoveConstructibleDestructible,则行为未定义。

内容

[编辑] 参数

f - Callable 对象(函数对象、指向函数的指针、指向函数的引用、指向成员函数的指针或指向数据成员的指针),将被绑定到一些参数
args - 要绑定的参数列表,其中未绑定参数被替换为 占位符 _1_2_3... 属于 std::placeholders 命名空间

[edit] 返回值

一个类型为 T 的函数对象 g,对于它来说,std::is_bind_expression<T>::valuetrue。它具有以下成员

std::bind 返回类型

成员对象

std::bind 的返回类型包含一个类型为 std::decay<F>::type 的成员对象,该对象由 std::forward<F>(f) 构建,每个 args... 都有一个对象,其类型为 std::decay<Arg_i>::type,同样由 std::forward<Arg_i>(arg_i) 构建。

构造函数

如果 std::bind 的所有成员对象(如上所述)都是 可复制构造的,则 std::bind 的返回类型是 CopyConstructible,否则是 MoveConstructible。该类型定义了以下成员

成员类型 result_type

1) (C++17 中已弃用) 如果 F 是指向函数的指针或指向成员函数的指针,则 result_typeF 的返回类型。如果 F 是一个类类型,并且嵌套 typedef result_type,则 result_typeF::result_type。否则,不会定义 result_type
2) (C++17 中已弃用) result_type 正好是 R
(直到 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))

其中 fd 是类型为 std::decay<F>::type 的值,绑定参数 v1, v2, ..., vN 的值和类型按 下方 所述确定。

如果对 g() 的调用中提供的一些参数与存储在 g 中的任何占位符不匹配,则未使用的参数将被计算并丢弃。

调用 operator()不抛出异常的 或是一个 常量子表达式(自 C++20 起),当且仅当基础 INVOKE 操作也是不抛出异常的。如果 INVOKE 操作在被视为未计算的操作数时是格式良好的,则 operator() 将参与重载解析。

如果 gvolatile 限定的,则程序格式不正确。

如果对于任何可能的值 w1, w2, ..., wNINVOKE(fd, w1, w2, ..., wN) 从来无法成为有效的表达式,则行为未定义。

[edit] 绑定参数

对于每个存储的参数 arg_iINVOKEINVOKE<R> 操作中的相应绑定参数 v_i 按如下方式确定

[edit] 情况 1:引用包装器

如果 arg_i 的类型为 std::reference_wrapper<T>(例如,std::refstd::cref 在对 std::bind 的初始调用中使用),则 v_iarg_i.get(),它的类型 V_iT&:存储的参数按引用传递给调用的函数对象。

[edit] 情况 2:绑定表达式

如果 arg_i 的类型为 T,对于它来说,std::is_bind_expression<T>::valuetrue(例如,另一个 std::bind 表达式直接传递给对 std::bind 的初始调用),则 std::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 相同)。

[edit] 情况 3:占位符

如果 arg_i 的类型为 T,且 std::is_placeholder<T>::value 不为 0(意味着,在对 std::bind 的初始调用中,使用了诸如 std::placeholders::_1, _2, _3, ... 之类的占位符作为参数),则占位符指示的参数(u1 用于 _1u2 用于 _2,等等)将传递给可调用对象:v_istd::forward<Uj>(uj),其类型 V_iUj&&

[edit] 情况 4:普通参数

否则,arg_i 将作为左值参数传递给可调用对象:v_i 仅仅是 arg_i,其类型 V_iT cv &,其中 cvg 的 cv 限定符相同。

[edit] 异常

仅当从 std::forward<F>(f) 构造 std::decay<F>::type 抛出异常,或者从相应的 std::forward<Arg_i>(arg_i) 构造 std::decay<Arg_i>::type 的任何构造函数抛出异常时才会抛出异常,其中 Arg_i 是第 i 个类型,arg_iArgs... args 中的第 i 个参数。

[edit] 注释

Callable 中所述,在调用指向非静态成员函数或指向非静态数据成员的指针时,第一个参数必须是对对象的引用或指针(可能包括诸如 std::shared_ptrstd::unique_ptr 之类的智能指针),该对象将被访问其成员。

传递给 bind 的参数会被复制或移动,并且绝不会通过引用传递,除非被包裹在 std::refstd::cref 中。

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

[edit] 示例

#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;  // for _1, _2, _3...
 
    std::cout << "1) argument reordering and pass-by-reference: ";
    int n = 7;
    // (_1 and _2 are from std::placeholders, and represent future
    // arguments that will be passed to f1)
    auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n);
    n = 10;
    f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused
                    // makes a call to f(2, 42, 1, n, 7)
 
    std::cout << "2) achieving the same effect using a lambda: ";
    n = 7;
    auto lambda = [&ncref = n, n](auto a, auto b, auto /*unused*/)
    {
        f(b, 42, a, ncref, n);
    };
    n = 10;
    lambda(1, 2, 1001); // same as a call to f1(1, 2, 1001)
 
    std::cout << "3) nested bind subexpressions share the placeholders: ";
    auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);
    f2(10, 11, 12); // makes a call to f(12, g(12), 12, 4, 5);
 
    std::cout << "4) bind a RNG with a distribution: ";
    std::default_random_engine e;
    std::uniform_int_distribution<> d(0, 10);
    auto rnd = std::bind(d, e); // a copy of e is stored in rnd
    for (int n = 0; n < 10; ++n)
        std::cout << rnd() << ' ';
    std::cout << '\n';
 
    std::cout << "5) bind to a pointer to member function: ";
    Foo foo;
    auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);
    f3(5);
 
    std::cout << "6) bind to a mem_fn that is a pointer to member function: ";
    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) bind to a pointer to data member: ";
    auto f5 = std::bind(&Foo::data, _1);
    std::cout << f5(foo) << '\n';
 
    std::cout << "8) bind to a mem_fn that is a pointer to data member: ";
    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) use smart pointers to call members of the referenced objects: ";
    std::cout << f6(std::make_shared<Foo>(foo)) << ' '
              << f6(std::make_unique<Foo>(foo)) << '\n';
}

输出

1) argument reordering and pass-by-reference: 2 42 1 10 7
2) achieving the same effect using a lambda: 2 42 1 10 7
3) nested bind subexpressions share the placeholders: 12 12 12 4 5
4) bind a RNG with a distribution: 0 1 8 5 5 2 0 7 7 10 
5) bind to a pointer to member function: 100
6) bind to a mem_fn that is a pointer to member function: 100
7) bind to a pointer to data member: 10
8) bind to a mem_fn that is a pointer to data member: 10
9) use smart pointers to call members of the referenced objects: 10 10

[edit] 缺陷报告

以下更改行为的缺陷报告被追溯应用于先前发布的 C++ 标准。

DR 应用于 已发布的行为 正确行为
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&&

[edit] 另见

(C++20)(C++23)
按顺序将可变数量的参数绑定到函数对象
(函数模板) [edit]
std::bind 表达式中未绑定参数的占位符
(常量) [edit]
(C++11)
从指向成员的指针创建函数对象
(函数模板) [edit]