原标题:包装器和绑定器std::bind和std::function的回调技术
原创
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
std::function作为回调函数,std::function配合std::bind和lambda表达式能够很方便的指向函数指针。C++中有如下几种可调用对象:函数、函数指针、lambda表达式、bind对象、函数对象。其中,lambda表达式和bind对象是C++11标准中提出的(bind机制并不是新标准中首次提出,而是对旧版本中bind1st和bind2st的合并)。个人认为五种可调用对象中,函数和函数指针本质相同,而lambda表达式、bind对象及函数对象则异曲同工。
插播一下函数指针和函数类型的区别:
函数指针指向的是函数而非对象。和其他指针类型一样,函数指针指向某种特定类型;
函数类型由它的返回值和参数类型决定,与函数名无关。
例如:
bool fun(int a, int b)
上述函数的函数类型是:bool(int, int)
上述函数的函数指针pf是:bool (*pf)(int, int)
一般对于函数来说,函数名即为函数指针:
#include
//被调用的普通函数
int Func1(int x,int y){
std::cout<
}
//有一个形参为函数指针
int Func2(int(*fp)(int,int),int x,int y){
return fp(x,y);
}
//定义一个函数指针类型为FType
typedef int (*FType)(int,int);
int Func3(FType fp,int x,int y){
return fp(x,y);
}
int main(){
Func2(Func1,100,100); //函数Func2调用函数Func1
Func3(Func1,200,200); //函数Func3调用函数Func1
return 0;
}
输出结果:
PS D:\bind和function的混合使用\build\函数指针\Debug> .\main.exe
200
400
PS D:\bind和function的混合使用\build\函数指针\Debug>
可以看出,函数指针作为参数,可以调用函数指针所指向的函数内容。
参考自《Linux多线程服务端编程》以及muduo源码,对其中的一些实现细节有着十分深刻的印象,尤其是使用std::bind和std::function的回调技术。可以说,这两个大杀器简直就是现代C++的“任督二脉”,甚至可以解决继承时的虚函数指代不清的问题。在此详细叙述使用std::bind和std::function在C++对象之间的用法,用以配合解决事件驱动的编程模型。
本文组成:
1.std::function
2.std::bind
3.使用std::bind和std::function的回调技术
4.std::bind绑定到虚函数时会表现出多态行为,解决继承时的虚函数指代不清的问题
function模板类和bind模板函数,使用它们可以实现类似函数指针的功能,但却却比函数指针更加灵活,特别是函数指向类 的非静态成员函数时。不过网上有文章简单测试:函数指针要比直接调用慢2s左右;std::function 要比函数指针慢2s左右 。
std::function位于头文件#include,可将各种可以调用实体进行封装统一。包括:
实例通过上述几种方式实现一个简单的比较两个数的大小的功能(读者可拷贝代码观察结果)代码如下:
#include
#include
#include
std::unordered_map
std::function
//1.普通函数
bool compare_com(int a,int b){
return a>b;
}
//lambda表达式
auto compare_lam=[](int a,int b)->bool{
return a>b;
};
//仿函数(函数对象)
class Compare_class{
public:
bool operator()(int a,int b){
return a>b;
}
};
//类成员函数(静态或者动态)
class Compare{
public:
bool compare_member(int a,int b){
return a>b;
}
static bool compare_static_member(int a,int b){
return a>b;
}
};
void Func1(){
func=compare_com;
bool flag=func(10,1);
std::cout<<"普通函数输出,flag is "<
void Func2(){
func=compare_lam;
bool flag=func(10,1);
std::cout<<"lambda表达式输出,flag is "<
void Func3(){
func=Compare_class();
bool flag=func(10,1);
std::cout<<"仿函数(函数对象输出),flag is "<
void Func4(){
func=Compare::compare_static_member;
bool flag=func(10,1);
std::cout<<"类静态成员函数输出,flag is "<
void Func5(){
Compare tmp;
func=std::bind(&www.yii666.comamp;Compare::compare_member,tmp,std::placeholders::_1,std::placeholders::_2);
bool flag=func(10,1);
std::cout<<"类普通成员函数输出,flag is "<
int main(){
Func1();
Func2();
Func3();
Func4();
Func5();
return 0;
}
输出结果:
普通函数输出,flag is 1
lambda表达式输出,flag is 1
仿函数(函数对象输出),flag is 1
类静态成员函数输出,flag is 1
类普通成员函数输出,flag is 1
PS D:\bind和function的混合使用\build\function\Debug>
由上文可以看出:由于可调用对象的定义方式比较多,但是函数的调用方式较为类似,因此需要使用一个统一的方式保存可调用对象或者传递可调用对象。于是,std::function就诞生了。
std::function是一个可调用对象包装器,是一个类模板,可以容纳除了类成员函数指针之外的所有可调用对象,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟它们的执行。
定义function的一般形式:
# include
std::function<函数类型>
std::function可以取代函数指针的作用,因为它可以延迟函数的执行,特别适合作为回调函数使用。它比普通函数指针更加的灵活和便利。
故而,std::function的作用可以归结于:
std::bind可以看作一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来适应原对象的参数列表。
std::bind将可调用对象与其参数一起进行绑定,绑定后的结果可以使用std::function保存。std::bind主要有以下两个作用:
auto newCallable = bind(callable, arg_list);
该形式表达的意思是:当调用newCallable时,会调用calla文章来源地址20890.htmlble,并传给它arg_list中的参数。
需要注意的是:arg_list中的参数可能包含形如_n的名字。其中n是一个整数,这些参数是占位符,表示newCallable的参数,它们占据了传递给newCallable的参数的位置。数值n表示生成的可调用对象中参数的位置:_ _ 1为newCallable的第一个参数,_ 2为第二个参数,以此类推。
std::bind函数将可调用对象(开头所述6类)和可调用对象的参数进行绑定,返回新的可调用对象(std::function类型,参数列表可能改变),返回的新的std::function可调用对象的参数列表根据bind函数实参中std::placeholders::_x从小到大对应的参数确定。
实例详细说明返回的新的std::function可调用对象的参数列表如何确定:
#include
#include
struct Int{
int a;
};
bool compare_com(struct Int a,float b){
return a.a>b;
}
void Func1(){
Int a{3};
//Int a={3};
//placeholders::_1对应float,placeholders::_2对应struct Int所以返回值func返回值得类型为function
std::function
bool flag=func(2.0,a);
std::cout<<"flag is "<
int main(){
Func1();
return 0;
}
输出结果:
flag is 1
PS D:\bind和fu文章来源站点https://www.yii666.com/nction的混合使用\build\bind\Debug>
由此例子可以看出:
std::bind的返回值是可调用实体,可以直接赋给std::function。
C++的类成员函数不能像普通函数那样用于回调,因为每个成员函数都需要有一个对象实例去调用它。
通常情况下,要实现成员函数作为回调函数:
**一种过去常用的方法就是把该成员函数设计为静态成员函数(因为类的成员函数需要隐含的this指针 而回调函数没有办法提供)。**但这样做有一个缺点,就是会破坏类的结构性,因为静态成员函数只能访问该类的静态成员变量和静态成员函数,不能访问非静态的。
要解决这个问题,可以把对象实例的指针或引用做为参数传给它。后面就可以靠这个对象实例的指针或引用访问非静态成员函数。
下面的所有讨论基于对象。
代码如下:
#include
#include
typedef std::function
class Blas{
public:
//类成员函数
virtual void add(int a,int b){
std::cout< }
//类静态成员函数
static void addStatic(int a,int b){
std::cout< }
};
class BBlas:public Blas{
public:
virtual void add(int a,int b){
std::cout< }
};
void Func1(){
//Blas blas
BBlas bblas;
//使用std::bind绑定类静态成员函数
func f1(std::bind(&Blas::addStatic,1,2));
f1();
//使用std::bind绑定类的成员函数 对象加地址与不加地址实现结果一样
//func f2(std::bind(&Blas::add,&bblas,1,2));
func f2(std::bind(&Blas::add,bblas,1,2));
f2();
}
int main(){
Func1();
return 0;
}
输出结果:
3this Blas
3this BBlas
PS D:\bind和function的混合使用\build\bind和function混合\Debug>
上述代码中的区别是:如果不是类的静态成员函数,需要在参数绑定时,往绑定的参数列表中加入使用的对象。
另一种办法就是使用std::bind和std::function结合实现回调技术。
#include
#include
typedef std::function
class Blas{
public:
void SetCallBack(const func& cb){
cb_=cb;
}
void PrintFunctor(){
cb_();
}
private:
func cb_;
};
class Atlas{
public:
Atlas(int x)
:x_(x){
//使用当前类的静态成员函数
blas_.SetCallBack(std::bind(&AddStatic,x,2));
//使用当前类的非静态成员函数
blas_.SetCallBack(std::bind(&Atlas::Add,this,x,2));
}
void Print(){
blas_.PrintFunctor();
}
private:
void Add(int a,int b){
std::cout< }
static void AddStatic(int a,int b){
std::cout< }
private:
Blas blas_;
int x_;
};
void Func1(){
Atlas atlas(5);
atlas.Print();
}
int main(){
Func1();
return 0;
}
输出结果:
7
两个函数在Atlas类中,并且可以自由操作Atlas的数据成员。尽管是将add()系列的函数封装成函数对象传入Blas中,并且在Blas类中调用,但是它们仍然具有操作Atlas数据成员的功能,在两个类之间形成了弱的耦合作用。但是如果要在两个类之间形成弱的耦合作用,必须在使用std::bind()封装时,向其中传入this指针。
在 C++中, 可以像函数一样调用的有: **普通函数、类的静态成员函数、仿函数、 lambda 函数、类的非静态成员函数、 可被转换为函数的类的对象,**统称可调用对象或函数对象。
可调用对象有类型, 可以用指针存储它们的地址, 可以被引用(类的成员函数除外) 。
普通函数类型可以声明函数、 定义函数指针和函数引用, 但是, 不能定义函数的实体。
#include
#include
using Func=void(int,const std::string&); //普通函数的别名
Func Show; //声明普通函数
//void Show(int,const std::string&);
//定义普通函数
void Show(int n,const std::string& str){
std::cout<<"亲爱的"<
int main(){
Show(1,"我是一只笨鸟"); //直接调用普通函数
void(*fp1)(int,const std::string&)=Show; //声明函数指针,指向Show这个函数
void(&fr1)(int,const std::string&)=Show; //声明函数引用,引用Show这个函数
fp1(2,"我是一只笨鸟"); //用函数指针调用Show普通函数
fr1(3,"我是一只笨鸟"); //用函数引用调用普通函数
Func* fp2=Show; //声明函数指针,指向Show普通函数
Func& fr2=Show; //声明函数引用,指向Show普通函数
fp2(4,"我是一只笨鸟"); //用声明的函数指针调用普通函数
fr2(5,"我是一只笨鸟"); //用声明的函数引用调用普通函数
return 0;
}
输出结果:
PS D:\可调用对象、包装器function、绑定器bind\Debug> .\main.exe
亲爱的1, 我是一只笨鸟
亲爱的2, 我是一只笨鸟
亲爱的3, 我是一只笨鸟
亲爱的4, 我是一只笨鸟
亲爱的5, 我是一只笨鸟
PS D:\可调用对象、包装器function、绑定器bind\Debug>
类的静态成员函数和普通函数本质上是一样的,把普通函数放在类中而已。
#include
#include
using Func=void(int ,const std::string& ); //普通函数起别名
struct AA{
//类中有静态成员函数
static void Show(int n,const std::string& str){
std::cout<<"亲爱的 "<
};
int main(){
AA::Show(1,"我是一只笨鸟 "); //直接用类::调用静态成员函数
void(*fp1)(int,const std::string&)=AA::Show; //用函数指针指向静态成员函数
void(&fr1)(int,const std::string&)=AA::Show; //用函数引用引用静态成员函数
fp1(2,"我是一只笨鸟 "); //用函数指针调用静态成员函数
fr1(3,"我是一只笨鸟 "); //用函数引用调用静态成员函数
Func* fp2=AA::Show; //定义一个函数指针指向静态成员函数
Func& fr2=AA::Show; //定义一个函数引用指向静态成员函数
fp2(4,"我是一只笨鸟 "); //用函数指针调用静态成员函数
fr2(5,"我是一只笨鸟 "); //用函数引用调用静态成员函数
return 0;
}
输出结果:
亲爱的 1, 我是一只笨鸟
亲爱的 2, 我是一只笨鸟
亲爱的 3, 我是一只笨鸟
亲爱的 4, 我是一只笨鸟
亲爱的 5, 我是一只笨鸟
PS D:\可调用对象、包装器function、绑定器bind\Debug> ^C
PS D:\可调用对象、包装器function、绑定器bind\Debug>
仿函数的本质是类,调用的代码像函数。仿函数的类型就是类的类型。
#include
#include
struct BB{
void operator()(int n,const std::string& str){
std::cout<<"亲爱的 "<
};
int main(){
BB bb;
bb(11,"我是一只笨鸟 "); //用对象调用仿函数(函数对象)
BB()(12,"我是一只笨鸟 "); //用匿名函数对象调用仿函数(函数对象)
BB& br=bb; //对象的引用绑定bb对象
br(13,"我是一只笨鸟 "); //用对象的引用调用仿函数
return 0;
}
输出结果:
PS D:\可调用对象、包装器function、绑定器bind\Debug> .\main.exe
亲爱的 11, 我是一只笨鸟
亲爱的 12, 我是一只笨鸟
亲爱的 13, 我是一只笨鸟
PS D:\可调用对象、包装器function、绑定器bind\Debug>
lambda 函数的本质是仿函数, 仿函数的本质是类。
#include
#include
int main(){
//创建lambda对象
auto lb=[](int n,const std::string& str)->void{
std::cout<<"亲爱的 "<
auto& lr=lb; //引用lambda对象
lb(1,"我是一只笨鸟 "); //文章来源地址20890.html直接用lambda对象调用仿函数
lr(2,"我是一只笨鸟 "); //用lambda对象的引用调用仿函数
return 0;
}
输出结果:
PS D:\可调用对象、包装器function、绑定器bind\Debug> .\main.exe
亲爱的 1 ,我是一只笨鸟
亲爱的 2 ,我是一只笨鸟
PS D:\可调用对象、包装器function、绑定器bind\Debug>
类的非静态成员函数有地址,但是,只能通过类的对象才能调用它,所以, C++对它做了特别处理。
类的非静态成员函数只有指针类型,没有引用类型,不能引用。
#include
#include
struct CC{
//类中有普通函数
void Show(int n,const std::string& str){
std::cout<<"亲爱的 "<
};
int main(){
CC cc;
cc.Show(14,"我是一只笨鸟 "); //直接用类的对象去调用类中方法
void(CC::*fp1)(int,const std::string&)=&CC::Show; //定义类的成员函数指针指向类的成员函数
(cc.*fp1)(15,"我是一只笨鸟 "); //用类的成员函数指针调用成员函数
using pFunc=void(CC::*)(int,const std::string&); //类成员函数指针起别名
pFunc fp2=&CC::Show; //类成员函数指针指向类成员函数
(cc.*fp2)(16,"我是一只笨鸟 ");
//cc.*fp2就是解引用出这个函数出来,得到这个函数
return 0;
}
输出结果:
PS D:\可调用对象、包装器function、绑定器bind\Debug> .\main.exe
亲爱的 14, 我是一只笨鸟
亲爱的 15, 我是一只笨鸟
亲爱的 16, 我是一只笨鸟
PS D:\可调用对象、包装器function、绑定器bind\Debug>
类可以重载类型转换运算符 operator 数据类型() ,如果数据类型是函数指针或函数引用类型,那么该类实例也将成为可调用对象。
它的本质是类,调用的代码像函数。
在实际开发中,意义不大。
#include
#include
//定义函数
void Show(int n,const std::string& str){
std::cout<<"亲爱的 "<
struct DD{
//可以被转换成函数指针的类
using Func=void(*)(int,const std::string&);
operator Func(){
return Show;
}
};
int main(){
DD dd;
dd(17,"我是一只笨鸟 ");
return 0;
}
输出结果:
PS D:\可调用对象、包装器function、绑定器bind\Debug> .\main.exe
亲爱的 17 ,我是一只笨鸟
PS D:\可调用对象、包装器function、绑定器bind\Debug>
包装器function
std::function 模板类是一个通用的可调用对象的包装器,用简单的、 统一的方式处理可调用对象。
template
class function……
Fty 是可调用对象的类型,格式: 返回类型(参数列表)。
包含头文件: #include
注意:
⚫ 重载了 bool 运算符,用于判断是否包装了可调用对象。
⚫ 如果 std::function 对象未包装可调用对象, 使用 std::function 对象将抛出 std::bad_function
call 异常。
#include
#include
#include
//普通函数
void Show(int n,const std::string& str){
std::cout<<"亲爱的 "<
struct AA{
//类中静态成员函数
static void Show(int n,const std::string& str){
std::cout<<"亲爱的 "<
};
struct BB{
//类中仿函数
void operator()(int n,const std::string& str){
std::cout<<"亲爱的 "<
};
struct CC{
//类的普通函数
void Show(int n,const std::string& str){
std::cout<<"亲爱的 "<
};
struct DD{
//类中可以被转换普通函数的类
using Func=void(*)(int,const std::string& );
operator Func(){
return Show; //返回普通函数的地址
}
};
int main(){
using Func=void(int,const std::string& str); //函数类型的别名
//普通函数
void(*fp1)(int,const std::string&)=Show; //声明函数指针,指向函数对象
fp1(1,"我是一只笨鸟 "); //用函数指针调用普通函数
std::function
fn1(1,"我是一只笨鸟"); //用function对象调用普通函数
//类中静态成员函数
void(*fp2)(int,const std::string&)=AA::Show; //声明函数指针指向类中成员函数对象
fp2(2,"我是一只笨鸟 "); //用函数指针调用类中成员函数
std::function
fn2(2,"我是一只笨鸟"); //用function对象调用类中静态成员函数
//仿函数
BB bb;
bb(3,"我是一只笨鸟 "); //用仿函数对象调用仿函数
BB()(3,"我是一只笨鸟"); //仿函数匿名对象调用
std::function
fn3(3,"我是一只笨鸟 "); //用function对象调用仿函数
//lambad表达式
auto lb=[](int n,const std::string& str)->void{ //调用lambad表达式
std::cout<<"亲爱的 "<
lb(4,"我是一只笨鸟 ");
std::function
std::function
std::cout<<"亲爱的 "<
fn5(4,"我是一只笨鸟 ");
//类中非静态成员函数 注意这里包装的时候要加对象的地址
CC cc;
void(CC::*fp3)(int,const std::string&)=&CC::Show; //定义类成员函数指针指向类成员函数
(cc.*fp3)(5,"我是一只笨鸟 "); //用类成员函数指针调用类成员函数
std::function
fn6(cc,5,"我是一只笨鸟 "); //用function对象调用成员函数
//可以被转换为函数指针的对象
DD dd;
dd(6,"我是一只笨鸟 "); //用来被转换为函数指针的类对象的普通函数
std::function
fn7(6,"我是一只笨鸟 ");
std::function
try{
fn8(7,"我是一只笨鸟 ");
}catch(std::bad_function_call e){
std::cout<<"抛出了 std::bad_function_call异常";
}
return 0;
}
输出结果:
PS D:\可调用对象、包装器function、绑定器bind\Debug> .\main.exe
亲爱的 1, 我是一只笨鸟
亲爱的 1, 我是一只笨鸟
亲爱的 2, 我是一只笨鸟
亲爱的 2, 我是一只笨鸟
亲爱的 3 ,我是一只笨鸟
亲爱的 3 ,我是一只笨鸟
亲爱的 3 ,我是一只笨鸟
亲爱的 4, 我是一只笨鸟
亲爱的 4, 我是一只笨鸟
亲爱的 5, 我是一只笨鸟
亲爱的 5, 我是一只笨鸟
亲爱的 6, 我是一只笨鸟
亲爱的 6, 我是一只笨鸟
抛出了 std::bad_function_call异常
PS D:\可调用对象、包装器function、绑定器bind\Debug>
适配器(绑定器)bind
std::bind()模板函数是一个通用的函数适配器(绑定器) ,它用一个可调用对象及其参数,生成一个新的可调用对象,以适应模板。包含头文件: #include
函数原型:
template
function<> bind (Fx&& fx, Args&…args);
**Fx:**需要绑定的可调用对象(可以是前两节课介绍的那六种,也可以是 function 对象) 。
**args:**绑定参数列表, 可以是左值、 右值和参数占位符 std::placeholders::_n,如果参数不是占位符,缺省为值传递, std:: ref(参数)则为引用传递。
std::bind()返回 std::function 的对象。
std::bind()的本质是仿函数。
#include
#include
#include
// void Show(int n,const std::string& str){
// std::cout<<"亲爱的 "<
//普通函数
void Show(int n,const std::string& str){
std::cout<<"亲爱的 "<
struct AA{
//类中静态成员函数
static void Show(int n,const std::string& str){
std::cout<<"亲爱的 "<
};
struct BB{
//类中仿函数
void operator()(int n,const std::string& str){
std::cout<<"亲爱的 "<
};
struct CC{
//类的普通函数
void Show(int n,const std::string& str){
std::cout<<"亲爱的 "<
};
struct DD{
//类中可以被转换普通函数的类
using Func=void(*)(int,const std::string& );
operator Func(){
return Show; //返回普通函数的地址
}
};
int main(){
//std::bind返回的function模板对象,所有可以用auto fn1=Show接收之类的
//两种输出结果是一样的
std::function
std::function
fn1(1,"我是一只笨鸟 ");
fn2(1,"我是一只笨鸟 ");
//当现有的函数类型Show与要求的函数类型不一样
//意思是现有函数类型参数顺序不一样或者要求的函数类型数量少
//这个时候就要用到std::bind绑定器(适配器),
//用std::bind对现有函数对象进行转换,生成新的函数对象,与要求的函数对象类型匹配上
//如果不用std::bind(适配器)的话,要实现需求的话就只能重载原来的函数达到要求,比较麻烦
// std::function
// std::function
std::function
fn3("我是一只笨鸟 ",2);
//直接提前绑定编号3,发行对象只有一个参数对应一个std::placeholders::_1一个占位符
std::function
fn4("我是一只笨鸟 ");
int n=4;
//用std::bind()绑定的参数缺省的话是之值传递,用传引用的话用std::ref(n)
std::function
n=100;
fn5("我是一只笨鸟 ");
//要发行的对象参数比原来的函数对象多,std::bind绑定的时候不用管多余的参数,调用的时候随便加什么就行
std::function
fn6(5,"我是一只笨鸟",6);
//普通函数
std::function
fn7(6,"我是一只笨鸟 ");
//类的静态成员函数
std::function
fn8(7,"我是一只笨鸟 ");
//仿函数(函数对象)
std::function
fn9(8,"我是一只笨鸟 ");
//lambda表达式
std::function
std::cout<<"亲爱的 "<
fn10(9,"我是一只笨鸟 ");
//类的非静态成员函数
CC cc;
std::function
fn11(cc,10,"我是一只笨鸟 ");
//在实际开发中,对于类的非静态成员函数,发行对象调用的时候不希望把对象传过去,不适合模板
// 为了解决这个问题,可以把对象提前绑定
std::function
fn12(10,"我是一只笨鸟 ");
//可以被转换为函数指针的类对象
DD dd;
std::function
fn13(11,"我是一只笨鸟 ");
return 0;
}
输出结果:
亲爱的 1, 我是一只笨鸟
亲爱的 1, 我是一只笨鸟
亲爱的 2, 我是一只笨鸟
亲爱的 3, 我是一只笨鸟
亲爱的 100, 我是一只笨鸟
亲爱的 5, 我是一只笨鸟
亲爱的 6, 我是一只笨鸟
亲爱的 7, 我是一只笨鸟
亲爱的 8 ,我是一只笨鸟
亲爱的 9, 我是一只笨鸟
亲爱的 10, 我是一只笨鸟
亲爱的 10, 我是一只笨鸟
亲爱的 11, 我是一只笨鸟
PS D:\可调用对象、包装器function、绑定器bind\Debug>
写一个函数,函数的参数是函数对象及参数, 功能和 thread 类的构造函数相同。
thread类的构造函数可以接收不同的对象以及对象不同的参数。
#include
#include
#include
void Show1(){
//无参普通函数
std::cout<<"亲爱的,我是一只笨鸟 "<
void Show2(const std::string& str){
//带一个参数的普通函数
std::cout<<"亲爱的, "<
struct CC{
void Show3(int n,const std::string& str){
//类中带两个参数的普通函数,非静态成员函数
std::cout<<"亲爱的 "<
};
template
// void Show(Fn fn,Args...args){
//C++11一般要把返回类型写在后面
auto Show(Fn&& fn,Args&&...args)->decltype(std::bind(std::forward
std::cout<<"开始 "<
auto f=std::bind(std::forward
f();
std::cout<<"结束 "<
}
int main(){
std::cout<<"================"<
auto f1=std::bind(Show1);
f1();
Show(Show2,"我是一只笨鸟 ");
std::function
f2("我是一只笨鸟 ");
CC cc;
Show(&CC::Show3,&cc,3,"我是一只笨鸟 ");
std::function
f3(3,"我是一只笨鸟 ");
std::cout<<"================"<
// std::thread t2(Show1);
// std::thread t3(Show2,"我是一只笨鸟 ");
// CC cc;
// std::thread t4(&CC::Show3,&cc,3,"我是一只笨鸟 ");
// //t1.join();
// t2.join();
// t3.join();
// t4.join();
return 0;
}
输出结果:
PS D:\可调用对象、包装器function、绑定器bind\Debug> .\main.exe
================
开始
亲爱的,我是一只笨鸟
结束
亲爱的,我是一只笨鸟
开始
亲爱的, 我是一只笨鸟
结束
亲爱的, 我是一只笨鸟
开始
亲爱的 3, 我是一只笨鸟
结束
亲爱的 3, 我是一只笨鸟
================
PS D:\可调用对象、包装器function、绑定器bind\Debug>
回调函数的实现
如何取代虚函数
map和unorder_map
#include
#include
来源于:包装器和绑定器std::bind和std::function的回调技术
原创