• 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

  • 相关阅读:
    CCF——分蛋糕(2017-3)
    CCF——公共钥匙盒(2017-9)
    CCF——打酱油(2017-9)
    CCF——游戏(2017-12)
    SDS-简单动态字符串
    Redis主从复制
    MySQL 知识点
    MySQL 死锁
    Java 类加载机制
    Java IO
  • 原文地址:https://www.cnblogs.com/Braveliu/p/16864524.html
Copyright © 2020-2023  润新知