std::bind
- std::bind
- 1. 参数重排序和引用传递(argument reordering and pass-by-reference)
- 2. 嵌套绑定子表达式共享占位符(nested bind subexpressions share the placeholders)
- 3. 绑定一个随机数生成器于均匀离散分布器上(binding a RNG with a distribution)
- 4.绑定到成员函数的指针(bind to a pointer to member function)
- 5.绑定到成员函数指针包装器mem_fn(bind to a mem_fn that is a pointer to member function)
- 6. 绑定类成员变量
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