今天,我们来看看c++11中的一个可调用对象包装器——function以及在头文件<functional>下的bind函数。
一.functional
1.概念:
std::function是可调用对象的包装器。它是一个类模板,可以容纳除了类成员(函数)指针之外的所有可调用对象。通过指定它的模板参数,它可以用统一的方式处理函数,函数对象,函数指针,并允许保存和延迟执行它们。包装器function使其能够存储、复制、调用任何可复制构造的可调用对象1,包括全局函数、成员函数、函数对象、lambda表达式、bind表达式。
2.基本用法:
std::function必须要包含一个叫做functional的头文件,可调用对象包装器使用语法如下:
#include <functional>
std::function<返回值类型(参数类型列表)> diy_name = 可调用对象;
主要以下包装用途:
1.包装普通函数
2.包装类的静态函数
3.包装仿函数
4.包装转换成函数指针的对象
从下面例子中体会这四种用途吧
例子一:
#include <iostream>
#include <functional>
#include <string>
using namespace std;
void print(int num, string name)
{
cout << "id = "<<name << ",age = " << num << endl;
}
using funcptr = void(*)(int, string);
class Test
{
public:
static void world(int a, string s)
{
cout << "number = " << a << ",name = " << s << endl;
}
void operator()(string msg)
{
cout << "仿函数:" << msg << endl;
}
operator funcptr()
{
return world;
}
};
int main()
{
//1.包装普通函数
function<void(int, string)> f1 = print;
//2.包装类的静态方法
function<void(int, string)> f2 = Test::world;
//3.包装仿函数
Test ta;
function<void(string)> f3 = ta;
//4.包装转换成函数指针的对象
Test tb;
function<void(int, string)> f4 = tb;
//调用
f1(1, "ace");
f2(2, "tom");
f3("jack");
f4(3, "lily");
return 0;
}
例子二:
#include <iostream>
#include <functional>
#include <string>
using namespace std;
class A
{
public:
A(const function<void(int, string)> &f) :callback(f)
{
}
void notify(int id, string name)
{
callback(id, name);
}
private:
function<void(int, string)> callback;
};
void print(int num, string name)
{
cout << "id = " << name << ",age = " << num << endl;
}
using funcptr = void(*)(int, string);
class Test
{
public:
static void world(int a, string s)
{
cout << "number = " << a << ",name = " << s << endl;
}
void operator()(string msg)
{
cout << "仿函数:" << msg << endl;
}
operator funcptr()
{
return world;
}
};
int main()
{
//1.包装普通函数
function<void(int, string)> f1 = print;
//2.包装类的静态方法
function<void(int, string)> f2 = Test::world;
//3.包装仿函数
Test ta;
function<void(string)> f3 = ta;
//4.包装转换成函数指针的对象
Test tb;
function<void(int, string)> f4 = tb;
A aa(print);
aa.notify(1, "tom");
A ab(Test::world);
ab.notify(2, "jack");
A ac(tb);
ac.notify(3, "luf");
return 0;
}
二.bind
概念:bind是一个标准库函数,定义在functional头文件中。可以将bind函数看作一个通用的函数适配器,它接受一个可调用对象,生成新的可调用对象来适应原对象的参数列表。
调用bind的一般形式为:auto newCallable=bind(callable,arg_list)
newCallable和callable都是可调用对象,arg_list对应calllable的参数。当我们调用newCallable时,newCallable会调用callable,并传递给它arg_list中的参数
arg_list中的参数可能包含形如_n的占位符,如_1,_2,代表了newCallable中相应位置的参数
auto check6=bind(check_size,_1,6);当我们调用check6(str)的时候,实际会调用check_size(str,6)
bind作用:
1.改变参数个数:bind可以用于改变参数的个数(减少?),如原本check_size要传入两个参数,通过bind我们可以只传入一个参数,另外的参数则在bind时决定。
2.改变参数位置:bind还可用于改变参数位置,例如,有一个函数bool isGreater(int a,int b)用于判断第一个参数是否大于第二个参数,则auto isSmaller=bind(isGreater,_2,_1);
通过改变参数顺序,让bind返回的可调用对象具有相反的含义。isSmaller(1,2)将实际调用isGreater(2,1),返回true。
3.设置类成员函数为回调函数
回调函数往往通过函数指针来实现,而类的成员函数,多了一个隐含的参数this,所以直接赋值给函数指针会引起编译报错。通过bind可以解决此问题
class MyClass
{
public:
MyClass() :n(0) {};
~MyClass()=default;
void add(int i) { n += i; cout << n; }
private:
int n;
};
MyClass a;
auto f=bind(&test::incr,&a,_1);
MyClass a;
auto f = bind(&MyClass::add, &a, _1);
f(1);//输出1
f(2);//输出 3
4.bind绑定引用参数
默认情况下,bind的那些不是占位符的参数被拷贝到bind返回的可调用对象中,但有些参数无法拷贝,可以使用标准库中的ref函数,它返回给定参数的引用。
ostream& print(ostream& os, const string& s, char c)
{
return os << s << c;
}
print(cout, "123", '-');//打印123-
//
vector<string> strVec = {"abc","def"};
//for_each(strVec.begin(), strVec.end(), bind(print,cout,_1,'-'));将报错cout无法拷贝
for_each(strVec.begin(), strVec.end(), bind(print,ref(cout),_1,'-'));//输出abc-def-
这里说一下ref函数:ref可以在模板传参的时候传入引用,否则无法传递
好啦,关于function和bind就分享到这啦。
本贴为博主亲手整理。如有错误,请评论区指出,一起进步。谢谢大家的浏览.