std::function
仿函数对象
std::function<… …>用来声明函数对象的,换句话说,就和函数指针、Lambda表达式、函数名是一个东西。
std::bind就是将一个函数对象绑定成为另一个函数对象,std::bind的返回值类型是std::function。
头文件 #include <functional>
命名空间 std
、std::placeholders
bind使用时的注意细节:
① bind的第一个参数必须要加取地址符号&
② 必须加上using namespace std::placeholders,否则找不到占位符
代码是最好的老师,二话不说,先看std::bind、std::function使用的代码
void showAll(int a, double b, const std::string& c)
{
std::cout << a << "; " << b << "; " << c << std::endl;
}
void test()
{
using namespace std::placeholders;
std::function<void(int, double)> output =
std::bind(&showAll, _1, _2, "Kobe");
output(1, 2);
}
观看代码,可以很明显的看到下面规律:
① bind中占位符的个数 = 调用时传递的实参的个数 = function<>中参数类型的个数
② std::bind用于对函数对象进行适配,适配成std::function<void(int, double)>类型
std::bind
绑定器
(1)首先,std::bind也是一个函数模板,返回值是一个仿函数,也是可调用对象。它的作用与bind1st和bind2st类似,是这两个函数的加强版。但极大地提高了灵活性,可以完全替代bind1st和bind2nd。
(2)bind的作用主要就是将可调用对象
变成std::function对象(即仿函数对象)
,主要体现在两个方面。
①将多元的可调用对象
与其参数一起绑定成一个仿函数对象
。
②将多元(设参数个数为n)的可调用对象
转成一元或(n-1)元的可调用对象
,即只绑定部分参数。
(3)std::bind可以绑定的对象(注意bind的返回值是仿函数)
①全局函数、静态全局函数 √
②类的static成员函数√
③类的非static成员函数(注意:_1必须是某个对象的地址)
④函数对象 / 仿函数- STL的仿函数、std::function √
⑤类的数据成员(注意:_1必须是某个对象的地址)
下面通过代码逐个展示使用方法:
①全局函数、静态全局函数 √
static void show(const std::string& a, const std::string& b, const std::string& c)
{
std::cout << a << "; " << b << "; " << c << std::endl;
}
int main()
{
using namespace std::placeholders; // 由于std::placeholders,可以将参数传递给绑定函数的顺序更改
auto x = bind(&show, _1, _2, _3);
auto y = bind(&show, _3, _1, _2);
auto z = bind(&show, "hello", _2, _1);
auto xx = bind(&show, _3, _3, _3);
x("one", "two", "three"); // one; two; three
y("one", "two", "three"); // three; one; two
z("one", "two"); // hello; two; one
xx("one", "two", "three"); // three; three; three
}
②类的static成员函数√
类的static成员函数不属于类对象,它属于整个类,并且static成员函数没有隐含的第一个this参数
结论:与①相比,对类的static成员函数bind时,只需要加上作用域A::,其他的都一样
class A{
public:
static void show(const std::string& a, const std::string& b, const std::string& c)
{
std::cout << a << "; " << b << "; " << c << std::endl;
}
};
int main()
{
using namespace std::placeholders; // 由于std::placeholders,可以将参数传递给绑定函数的顺序更改
auto x = bind(&A::show, _1, _2, _3);
auto y = bind(&A::show, _3, _1, _2);
auto z = bind(&A::show, "hello", _2, _1);
auto xx = bind(&A::show, _3, _3, _3);
x("one", "two", "three"); // one; two; three
y("one", "two", "three"); // three; one; two
z("one", "two"); // hello; two; one
xx("one", "two", "three");// three; three; three
return 0;
}
③类的非static成员函数(注意:_1必须是某个对象的地址)
类的非static成员函数属于类对象,它第一个参数是隐含的,即指向自身的指针this
结论: 对类的非static成员函数bind时,除了需要加上作用域A::之外,还要多加一个类对象参数
class A{
public:
void show(const std::string& a, const std::string& b, const std::string& c)
{
std::cout << a << "; " << b << "; " << c << std::endl;
}
};
int main()
{
using namespace std::placeholders; // 由于std::placeholders,可以将参数传递给绑定函数的顺序更改
A aa;
auto x = bind(&A::show, aa, _1, _2, _3); //多加一个类对象
auto y = bind(&A::show, aa, _3, _1, _2);
auto z = bind(&A::show, aa, "hello", _2, _1);
auto xx = bind(&A::show, aa, _3, _3, _3);
x("one", "two", "three"); // one; two; three
y("one", "two", "three"); // three; one; two
z("one", "two"); // hello; two; one
xx("one", "two", "three");// three; three; three
return 0;
}
④函数对象 / 仿函数- STL的仿函数、std::function √
#include <iostream>
#include <vector>
#include <algorithm> //for count_if
#include <functional> // for std::bind
using namespace std;
using namespace std::placeholders;
int main()
{
vector<int> v{ 15, 37, 94, 50, 73, 58, 28, 98 };
//通过bind2nd绑定仿函数less<int>()的第2参数为50
int n = count_if(v.begin(), v.end(), not1(bind2nd(less<int>(), 50)));
cout << "n = " << n << endl; //5(大于等于50的元素个数)
//通过bind绑定仿函数less<int>()的第2参数为50
cout << count_if(v.begin(), v.end(), bind(less<int>(), _1, 50)) << endl; //3
//通过bind绑定
//查找(50,73]之间的元素个数
// logical_and-逻辑与
auto f = bind(logical_and<bool>(), bind(greater<int>(), _1, 50), bind(less_equal<int>(), _1, 73));
cout << count_if(v.begin(), v.end(), f) << endl; //2
return 0;
}
示例代码1
#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;
class Test{
public:
typedef std::function<void(int, int)> pFUNC; //定义一个函数类型
Test() :x_(0), y_(0){}
Test(int x, int y, pFUNC pfunc) :x_(x), y_(y), pfunc_(pfunc){}
void run(){
pfunc_(x_, y_);
}
void showAll03(int x, int y, int z, int m, int n)
{
printf("%d,%d,%d,%d,%d\n", x, y, z, m, n);
}
private:
int x_, y_;
pFUNC pfunc_;
};
void showAll01(int x, int y, int z){
printf("%d,%d,%d\n", x, y, z);
}
void showAll02(int x, int y)
{
printf("%d,%d\n", x, y);
}
int main(){
int i, j;
cout << "请输入i j :";
cin >> i >> j;
Test aa(i, j, std::bind(showAll01, 10, _1, _2)); //将showAll函数,适配成CallBack类型
aa.run();
Test bb(i, j, std::bind(showAll02, _1, _2));
bb.run();
Test dd;
Test cc(i, j, std::bind(&Test::showAll03, &dd, 100, 200, _1, 300, _2));
cc.run();
}
示例代码2
本案例不是使用面向对象的编程思想,而是使用基于接口的设计思想。
企鹅能游泳,也能跑,麻雀能飞,此时需要设计一个类,既能像企鹅那样会游泳、跑,也能像麻雀那样会飞。如果使用多继承(继承企鹅、麻雀),耦合的比较紧密,使用std::bind、std::function可以避免使用继承,是一种基于接口的设计思想。
在新类Foo中,
① 添加std::function成员变量
② 在Foo构造函数中,给成员变量赋值
③ 在main函数中,调用构造函数创建Foo对象,使用到bind
Foo m_foo(
bind(&Penguin::run, &peg),
bind(&Penguin::swim, &peg),
bind(&Sparrow::fly, &spr)
);
全部代码:
#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;
//企鹅能游泳,也能跑
class Penguin
{
public:
void run(){ cout << "run..." << endl; }
void swim(){ cout << "swim..." << endl; }
};
//麻雀能飞
class Sparrow
{
public:
void fly(){ cout << "fly..." << endl; }
};
// 一个既用到run,也用到fly的客户class
class Foo{
public:
typedef std::function<void()> RUN;
typedef std::function<void()> SWIM;
typedef std::function<void()> FLY;
Foo(RUN run, SWIM swim, FLY fly) :
m_run(run), m_swim(swim), m_fly(fly)
{ }
void run()
{
m_run(); //调用回调函数
}
void swim()
{
m_swim();
}
void fly()
{
m_fly();
}
private:
RUN m_run;
SWIM m_swim;
FLY m_fly;
};
int main()
{
Penguin peg;
Sparrow spr;
Foo m_foo(
bind(&Penguin::run, &peg),
bind(&Penguin::swim, &peg),
bind(&Sparrow::fly, &spr)
);
m_foo.run();
m_foo.swim();
m_foo.fly();
return 0;
}