std::bind

std::bind

1. 参数重排序和引用传递(argument reordering and pass-by-reference)

1.1 示例代码

#include <iostream>
#include <functional>

void func(int n1, int n2, int n3, const int& n4, int n5)
{
    std::cout << "n1: " << n1 << std::endl;
    std::cout << "n2: " << n2 << std::endl;
    std::cout << "n3: " << n3 << std::endl;
    std::cout << "n4: " << n4 << std::endl;
    std::cout << "n5: " << n5 << std::endl;
}

int main()
{
    using namespace std::placeholders;  // for _1, _2, _3...

    int n = 7;
    auto bFunc = std::bind(func, _2, 43, _1, std::cref(n), n);
    n = 99;
    bFunc(33, 55, 1001);

    return 0;
}

/* run output:
n1: 55
n2: 43
n3: 33
n4: 99
n5: 7
*/

1.2 解析

1.2.1 参数重排序

从示例第18行分析,绑定函数func时,func函数五个参数:

第一个参数:_2,表示取实参列表中第二个值;结果n1等于55。

第二个参数:43,表示是个常量值,即固定值;结果n2等于43。

第三个参数:_1,表示取实参列表中第一个值;结果n3等于33。

第四个参数:std::cref(n),表示引用传递,引用n变量的值,即在调用函数前,n变为多少将来就取多少。

​ 第19行,我们将n赋值为99。结果n4等于19。

第五个参数:n,表示值传递,即就取此时此刻n变量的值,常量值为7,固定值。结果n5等于7。

注意:实参列表中第三个值1001没有使用到,1001等价于的std::placeholders_3值。

因为绑定时只使用到std::placeholders_1和std::placeholders_2的值。

1.2.2 引用传递

std::cref() 表示常量引用

std::ref 表示引用

1.3 使用lambda实现同样效果

1.3.1 示例代码

#include <iostream>

void func(int n1, int n2, int n3, const int& n4, int n5)
{
    std::cout << "n1: " << n1 << std::endl;
    std::cout << "n2: " << n2 << std::endl;
    std::cout << "n3: " << n3 << std::endl;
    std::cout << "n4: " << n4 << std::endl;
    std::cout << "n5: " << n5 << std::endl;
}

int main()
{
    int n = 7;
    auto lambda = [&ncref = n, n](auto a, auto b, auto unused) {
        func(b, 43, a, ncref, n);
    };
    n = 99;
    lambda(33, 55, 1001);

    return 0;
}

/* run output:
n1: 55
n2: 43
n3: 33
n4: 99
n5: 7
*/

2. 嵌套绑定子表达式共享占位符(nested bind subexpressions share the placeholders)

2.1 示例代码

#include <iostream>
#include <functional>

int g(int n1)
{
    return n1;
}

void func(int n1, int n2, int n3, const int& n4, int n5)
{
    std::cout << "n1: " << n1 << std::endl;
    std::cout << "n2: " << n2 << std::endl;
    std::cout << "n3: " << n3 << std::endl;
    std::cout << "n4: " << n4 << std::endl;
    std::cout << "n5: " << n5 << std::endl;
}

int main()
{
    using namespace std::placeholders;  // for _1, _2, _3...

    auto bFunc2 = std::bind(func, _3, std::bind(g, _2), _2, 4, 5);
    bFunc2(10, 11, 12);

    return 0;
}

/* run output:
n1: 12
n2: 11
n3: 11
n4: 4
n5: 5
*/

2.2 解析

从示例第22行分析,绑定函数func时,func函数五个参数:

第一个参数:_3,表示取实参列表中第三个值;结果n1等于12。

第二个参数:std::bind(g, _2),嵌套子表达式,绑定函数g,函数g的参数取值为std::placeholders_2,即实参列表中第二个值;g函数未对参数值改变。

结果n2等于11。

第三个参数:_2,表示取实参列表中第二个值;结果n3等于11。注意:与第二个参数共享std::placeholders_2实参列表中第二个值。

第四个参数:4,表示是个常量值,不论什么时候都不变化,即固定值;结果n4等于4。

第五个参数:5,表示是个常量值,不论什么时候都不变化,即固定值;结果n4等于5。

注意:实参列表中第一个值10没有使用到,10等价于的std::placeholders_1值。

因为绑定时只使用到std::placeholders_2和std::placeholders_3的值。

3. 绑定一个随机数生成器于均匀离散分布器上(binding a RNG with a distribution)

3.1 示例代码

#include <random>
#include <iostream>
#include <functional>

int main()
{
    // 生成随机数方式一:生成器作为参数
    {
        std::default_random_engine e;  //C++11提供的伪默认随机数生成器
        std::uniform_int_distribution<> d(0, 10); // 均匀生成(0,10)的离散整数
        for (int n = 0; n < 10; ++n)
        {
            std::cout << d(e) << ' ';  //生成一个随机数
        }
        std::cout << std::endl;
    }
    // 生成随机数方式二:利用std::bind进行绑定
    {
        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() << ' ';
        }
    }

    return 0;
}

/* run output:
6 3 10 9 4 7 0 6 5 4
6 3 10 9 4 7 0 6 5 4
*/

3.2 解析

std::default_random_engine C++11提供的一个伪默认随机数生成类。

std::uniform_int_distribution<> d(0, 10); 生成随机整型数,均匀分布于[0, 10],根据离散概率函数分析。

4.绑定到成员函数的指针(bind to a pointer to member function)

4.1 示例代码

#include <iostream>
#include <functional>

struct Foo
{
    void print_sum(int n1, int n2)
    {
        std::cout << n1 + n2 << '\n';
    }
};

int main()
{
    using namespace std::placeholders;  // for _1, _2, _3...

    Foo foo;
    // 方式一
    std::cout << "方式一:";
    foo.print_sum(95, 5);
    // 方式二
    std::cout << "方式二:";
    std::function<void(int)> f2 = std::bind(&Foo::print_sum, &foo, 95, _1);
    f2(5);
    // 方式三
    std::cout << "方式三:";
    auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);
    f3(5);

    return 0;
}

4.2 解析

绑定类的成员函数,必须首先明确绑定的类对象,类只是"形",对象才是“神”。

或者可以理解为:绑定必须通过实实在在的东西建立关系。即类Foo的对象foo的成员函数print_sum。

95为print_sum成员函数的第一个参数值,即为常量值,固定值。

std::placeholders_1即将实参第一个参数值作为print_sum成员函数的第二个参数值。

所以如上,第23、27行两种方式调用,均等价于第19行的效果。 估输出值均为100。

5.绑定到成员函数指针包装器mem_fn(bind to a mem_fn that is a pointer to member function)

5.1 示例代码

#include <iostream>
#include <functional>

struct Foo
{
    void print_sum(int n1, int n2)
    {
        std::cout << n1 + n2 << '\n';
    }
};

int main()
{
    using namespace std::placeholders;  // for _1, _2, _3...

    Foo foo;
    // 方式一
    std::cout << "方式一:";
    foo.print_sum(95, 5);
    // 方式二
    std::cout << "方式二:";
    std::function<void(int)> f2 = std::bind(&Foo::print_sum, &foo, 95, _1);
    f2(5);
    // 方式三
    std::cout << "方式三:";
    auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);
    f3(5);

    // 方式四(std::mem_fn && auto)
    {
        std::cout << "方式四:";
        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::mem_fn && 类型全称)
    {
        std::cout << "方式五:";
        std::_Mem_fn<void(Foo::*)(int, int)> ptr_to_print_sum = std::mem_fn(&Foo::print_sum);
        auto f5 = std::bind(ptr_to_print_sum, &foo, 95, _1);
        f5(5);
    }

    return 0;
}

/* run output:
方式一:100
方式二:100
方式三:100
方式四:100
方式五:100
*/

5.2 解析

std::mem_fn 这里的mem不是memory的缩写,而是member的缩写,即指类的成员member,而fn就是指function,其完整的单词应该是 member function,即mem_fn作用是用来包装类成员函数指针。

方式四和方式五无本质差异,方式五主要把ptr_to_print_sum变量的真实类型展现出来。便于理解和学习记忆。

6. 绑定类成员变量

6.1 示例代码

#include <iostream>
#include <functional>

struct Foo
{
    int m_nData = 100;
};

int main()
{
    using namespace std::placeholders;  // for _1, _2, _3...

    Foo foo;
    {
        // 方式一
        auto f1 = std::bind(&Foo::m_nData, _1);
        std::cout << "方式一 || 对象foo的成员变量m_nData的值:" << f1(foo) << '\n';
    }

    {
        // 方式二
        auto f2 = std::bind(&Foo::m_nData, _1);
        std::cout << "方式二 || 对象foo的成员变量m_nData的值:" << f2(&foo) << '\n';
    }

    {
        // 方式三
        auto ptr_to_data = std::mem_fn(&Foo::m_nData);
        auto f3 = std::bind(ptr_to_data, _1);
        int value = f3(&foo);
        std::cout << "方式三 || 对象foo的成员变量m_nData的值:" << value << '\n';
    }

    {
        // 方式四
        auto ptr_to_data = std::mem_fn(&Foo::m_nData);
        auto f4 = std::bind(ptr_to_data, _1);
        int value = f4(std::make_shared<Foo>(foo));
        std::cout << "方式四 || 对象foo的成员变量m_nData的值:" << value << '\n';
    }

    {
        // 方式五
        auto ptr_to_data = std::mem_fn(&Foo::m_nData);
        auto f5 = std::bind(ptr_to_data, _1);
        int value = f5(std::make_unique<Foo>(foo));
        std::cout << "方式五 || 对象foo的成员变量m_nData的值:" << value << '\n';
    }

    return 0;
}

/* run output:
方式一 || 对象foo的成员变量m_nData的值:100
方式二 || 对象foo的成员变量m_nData的值:100
方式三 || 对象foo的成员变量m_nData的值:100
方式四 || 对象foo的成员变量m_nData的值:100
方式五 || 对象foo的成员变量m_nData的值:100
*/

6.2 解析

方式一:常用的方式(推荐使用)。

方式二:调用时传入对象指针,方式一本质其实与方式二相同。

方式三:利用std::mem_fn,可见std::mem_fn不仅仅可以应用在类成员函数上,也可以利用在类成员变量上。

方式四:通过智能指针std::make_shared

方式五:通过智能指针std::make_unique

posted @ 2022-11-06 22:46  kaizenly  阅读(111)  评论(0编辑  收藏  举报
打赏