C++小利器之std::bind参数绑定包装器的使用详解
作者:ENG八戒
C++ 原生支持函数输入参数的默认值,但是有些业务场景下对原有设定的默认值不满意,那么可不可以临时改改?
请注意,随意改变原有代码逻辑,并不符合软件设计原则。有没有什么方案或者特性可以做到不修改原有代码逻辑,以实现新增参数默认值(或者在调用时绑定特性参数)的功能?
从 C++11 开始,标准库提供了 std::bind 用于绑定函数 f 和调用参数,返回一个新可调用函数对象 fn (也有喊它做仿函数的)。于是,在调用函数对象 fn 和输入新参数时,相当于调用了原来的函数 f 并自动按照绑定的关系映射参数,映射的方式非常灵活。这样被绑定映射的参数,实现原有函数 f 的参数新默认值功能,或者减少调用函数 f 时输入的参数个数等便捷需求。
简而言之,std::bind 被称为转发调用包装器。
绑定时,通过占位符 _1, _2, _3… ,指定在调用函数对象 fn 的输入参数和传递给函数 f 的参数之间的映射位置。
被绑定的函数 f 也分种类,那么,基于不同类型函数的应用示例是怎样的呢?
绑定静态或者全局函数
#include <functional> void output1(int a) { printf("%d\n", a); } void output2(int a, int b) { printf("%d %d\n", a, b); } int main(int argc, char * argv[]) { auto fn1 = std::bind(output1, std::placeholders::_1); fn1(1); auto fn2 = std::bind(output2, std::placeholders::_2, 0); fn2(3, 2); return 0; }
output:
1
2 0
占位符代表函数对象的输入参数,std::placeholders::_1 对应调用函数对象 fn 时的第 1 个输入参数,依次类推。
细心的你可能会留意 std::bind 返回的是可调用对象,这个和同样从 C++11 开始引入的 Lambda 表达式概念是类似的,那么有没有可能用 Lambda 表达式互相替换?试试改为使用 Lambda 表达式实现
int main(int argc, char * argv[]) { auto fn1 = [](int a) -> void { output1(a); }; fn1(1); auto fn2 = [](int a, int b) -> void { output2(b, 0); }; fn2(3, 2); return 0; }
这样子替换后,执行输出结果是一样的。
output:
1
2 0
绑定对象成员函数
如果被绑定的函数是对象成员,需要指定所属对象,看代码
#include <functional> class A { public: void output(int a, int b) { printf("%d %d\n", a, b); } }; int main(int argc, char * argv[]) { A obj; auto fn = std::bind(&A::output, &obj, std::placeholders::_2, std::placeholders::_1); fn(5, 3); return 0; }
output:
3 5
用 Lambda 表达式替换试试
int main(int argc, char * argv[]) { A obj; auto fn = [&obj](int a, int b) -> void { obj.output(b, a); }; fn(5, 3); return 0; }
output:
3 5
Notice:绑定的时候需要指定上下文环境(实例对象),这种场景已经涉及到闭包的概念了。由于上下文环境是有生命周期的,比如上面用到的实例对象 a,如果在超出了其生命周期的情况下,引用其内部已被释放的资源,是会引起程序执行异常的,这里提醒一下。
平替 Lambda
以上面两种场景示例代码的比较,在参数绑定的场景下,为了达到相同效果,Lambda 表达式的书写略显罗嗦,而 std::bind 的格式更有函数式编程的味道,可见,某些场景下推荐 std::bind 替代 Lambda 表达式。
能不能平替也分场景,三思而行。
到此这篇关于C++小利器之std::bind参数绑定包装器的使用详解的文章就介绍到这了,更多相关C++ std::bind内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!