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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异