C++11特性:可调用对象绑定器的使用
std::bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以使用std::function进行保存,并延迟调用到任何我们需要的时候。通俗来讲,它主要有两大作用:
1. 将可调用对象与其参数一起绑定成一个仿函数。
2. 将多元(参数个数为n,n>1)可调用对象转换为一元或者(n-1)元可调用对象,即只绑定部分参数。
绑定器函数使用语法格式如下:
// 绑定非类成员函数/变量
auto f = std::bind(可调用对象地址, 绑定的参数/占位符);
// 绑定类成员函/变量
auto f = std::bind(类函数/成员地址, 类实例对象地址, 绑定的参数/占位符);
这就解决了function不能包装类成员的弊端。?
?接下来来解释什么是占位符:
placeholders::_1是一个占位符,代表这个位置将在函数调用时被传入的第一个参数所替代。同样还有其他的占位符placeholders::_2、placeholders::_3、placeholders::_4、placeholders::_5等……
有了占位符的概念之后,使得std::bind的使用变得非常灵活:
#include <iostream>
#include <functional>
using namespace std;
void output(int x, int y)
{
cout << x << " " << y << endl;
}
int main(void)
{
// 使用绑定器绑定可调用对象和参数, 并调用得到的仿函数
bind(output, 1, 2)();
bind(output, placeholders::_1, 2)(10);
bind(output, 2, placeholders::_1)(10);
// error, 调用时没有第二个参数
// bind(output, 2, placeholders::_2)(10);
// 调用时第一个参数10被吞掉了,没有被使用
bind(output, 2, placeholders::_2)(10, 20);
bind(output, placeholders::_1, placeholders::_2)(10, 20);
bind(output, placeholders::_2, placeholders::_1)(10, 20);
return 0;
}
示例代码执行的结果:
1 2 // bind(output, 1, 2)();
10 2 // bind(output, placeholders::_1, 2)(10);
2 10 // bind(output, 2, placeholders::_1)(10);
2 20 // bind(output, 2, placeholders::_2)(10, 20);
10 20 // bind(output, placeholders::_1, placeholders::_2)(10, 20);
20 10 // bind(output, placeholders::_2, placeholders::_1)(10, 20);
?第几个参数也就是参数列表中的传入顺序。
下面来看一个关于绑定器的实际使用的例子:
#include <iostream>
#include <functional>
using namespace std;
void callFunc(int x, const function<void(int)>& f)//第三个参数是包装器类型
{
//传入的参数必须是一个可调用对象
//通过绑定器就可以得到可调用对象了
if (x % 2 == 0)
{
f(x);
}
}
void output(int x)
{
cout << x << " ";
}
void output_add(int x)
{
cout << x + 10 << " ";
}
int main(void)
{
// 使用绑定器绑定可调用对象和参数
//这里指定的是占位符,所以callFunc()里面的参数生效了
//如果占位符这里写上一个实参,那么callFunc()里面传的参也不会生效
auto f1 = bind(output, placeholders::_1);
for (int i = 0; i < 10; ++i)
{
callFunc(i, f1);
}
cout << endl;
auto f2 = bind(output_add, placeholders::_1);
for (int i = 0; i < 10; ++i)
{
callFunc(i, f2);
}
cout << endl;
return 0;
}
f1和f2是两个仿函数。
若你想要直接进行调用,就直接在bind函数后面加上()传递相应的参数就可以了,这时候f就不需要了。但是如果你想要进行包装,就得用到f。?
测试代码输出的结果:
0 2 4 6 8
10 12 14 16 18
在上面的程序中,使用了std::bind绑定器,在函数外部通过绑定不同的函数,控制了最后执行的结果。std::bind绑定器返回的是一个仿函数类型,得到的返回值可以直接赋值给一个std::function,在使用的时候我们并不需要关心绑定器的返回值类型,使用auto进行自动类型推导就可以了。
?
对于上述代码中占位符的进一步理解,我添加了对比结果输出的代码:
#include <iostream>
#include <functional>
using namespace std;
void callFunc(int x, const function<void(int)>& f)//第三个参数是包装器类型
{
//传入的参数必须是一个可调用对象
//通过绑定器就可以得到可调用对象了
if (x % 2 == 0)
{
f(x);
}
}
void output(int x)
{
cout << x << " ";
}
void output_add(int x)
{
cout << x + 10 << " ";
}
int main(void)
{
// 使用绑定器绑定可调用对象和参数
//这里指定的是占位符,所以callFunc()里面的参数生效了
//如果占位符这里写上一个实参,那么callFunc()里面传的参也不会生效
auto f1 = bind(output, placeholders::_1);
for (int i = 0; i < 10; ++i)
{
callFunc(i, f1);
}
cout << endl;
auto f2 = bind(output_add, placeholders::_1);
for (int i = 0; i < 10; ++i)
{
callFunc(i, f2);
}
cout << endl;
cout << "--------------------" << endl;
auto f11 = bind(output, 100);
for (int i = 0; i < 10; ++i)
{
callFunc(i, f11);
}
cout << endl;
auto f22 = bind(output_add, 200);
for (int i = 0; i < 10; ++i)
{
callFunc(i, f22);
}
cout << endl;
return 0;
}
运行结果为:
?
由此输出结果可以看出若bind绑定中已经有了固定的实参,后续再传参实参列表中的参数是无效的。只有在绑定的时候指定占位符,才会生效。
?
可调用对象包装器std::function是不能实现对类成员函数指针或者类成员指针的包装的,但是通过绑定器std::bind的配合之后,就可以完美的解决这个问题了,再来看一个例子,然后再解释里边的细节:
#include <iostream>
#include <functional>
using namespace std;
class Test
{
public:
void output(int x, int y)
{
cout << "x: " << x << ", y: " << y << endl;
}
int m_number = 100;
};
int main(void)
{
//因为是类的成员函数属于对象,所以要先创建对象
Test t;
// 绑定类成员函数
function<void(int, int)> f1 =
bind(&Test::output, &t, placeholders::_1, placeholders::_2);
// 绑定类成员变量(公共)
function<int& (void)> f2 = bind(&Test::m_number, &t);
// 调用
f1(520, 1314);
f2() = 2333;
cout << "t.m_number: " << t.m_number << endl;
return 0;
}
上述示例代码的运行结果为:
?
通过绑定类的成员函数和成员变量都会得到得到一个仿函数,这个仿函数就是一个可调用对象,使用包装器可以包装这个可调用对象中的仿函数。
注意对于成员变量,包装出来的变量,也是可读可写的,所以为引用类型,当然也可以使用auto自动推导类型。
需要注意的点:
但是使用auto自动推导和显示指定的类型是不相等的,因为绑定器绑定出来是一个仿函数,所以auto推导出来是仿函数,而显示指定得到的就是把仿函数进行包装的包装器类型,他们不是等价的,只不过是显示指定的做了一个隐式类型转换。?
占位符的编号是相对于原始函数的形参位置而言的,而不是相对于 std::bind
创建的函数对象(fn3)的形参位置。
例如:
// 普通函数
void show(int bh, const string& message) {
cout << "亲爱的" << bh << "号," << message << endl;
}
//参数调换位置
function<void(const string&, int)> fn3 = bind(show, placeholders::_2, placeholders::_1);
fn3("我是一只傻傻鸟。", 1);
这里输出的还是:亲爱的1号,我是一只傻傻鸟。?
本文参考:可调用对象包装器、绑定器 | 爱编程的大丙 (subingwen.cn)
?
?
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!