function函数对象、bind绑定器和lambda表达式
前置知识
-
C++中,将所有能当函数使用的对象都称为函数对象;
-
函数类型:函数的类型由返回值和形参一起决定
//函数 bool Compare(const string&,const string&); //函数类型 bool(const string&,const string&)
-
函数指针:
-
指向一个
函数类型所表示函数的指针
,无需解引用可以和函数使用一样正常调用:// 声明一个函数指针,指向返回值是bool类型, // 形参列表是(const string&,const string&)的函数 bool (*pf)(const string&,const string&); // 正常赋值 pf = Compare; //将pf指向Compare函数 pf = &Compare; // 等价于上面 // 函数指针的使用,下面三者等价 bool b1 = pf("hello","good"); bool b2 = (*pf)("hello","good"); bool b3 = Compare("hello","good");
-
指向不同函数类型的指针之间没有转换规则,函数指针赋值过程中,函数类型必须匹配
-
将函数名做参数传递给其他函数,函数名会转变成为函数指针,所以通过函数指针接收即可
-
-
仿函数/函数对象:通过函数类调用operator()重载,实现和函数一样的使用
-
函数对象和函数指针:
- 通过函数指针间接调用函数是无法内联的,导致函数调用开销,效率较低
- 使用函数对象,就可以进行内联
一、bind绑定器
1、前置
bind1st
和bind2nd
:将二元函数对象变成一元函数对象-
bind1st
:将函数对象的operator()的第一个参数绑定为一个确定的值,返回一个新得函数对象 -
bind2nd
:将函数对象的operator()的第二个参数绑定为一个确定的值,返回一个新得函数对象 -
局限性: 只能用于二元函数对象,不能用于普通函数,但是可以将普通函数通过
std::ptr_fun()
转化为函数对象(仿函数)#include <iostream> #include <functional> using namespace std; void foo(int a,int b) { std::cout << "Sum: " << a+b << std::endl; } int main() { auto bindFunc = std::bind1st(ptr_fun(foo),10); auto bindFunc2 = std::bind(foo, 10, placeholders::_1); bindFunc(20); // 调用 foo(10, 20) bindFunc2(2); // 调用 foo(10,2) return 0; }
-
2、C++11 bind绑定器
-
解决了
bind1st
和bind2nd
的二元函数的局限性,可以用于多元函数对象,最多可以有20个绑定参数,并且还可以绑定普通函数(退化为指针)、函数指针、成员函数指针等,返回一个新的函数对象,新的函数对象的具体类型取决于被绑定的参数 -
bind
绑定器占位符命名空间placeholders
:表示待输入的参数,可以调整传入的顺序namespace placeholders { _INLINE_VAR constexpr _Ph<1> _1{}; //表示第一个参数 _INLINE_VAR constexpr _Ph<2> _2{}; //表示第二个参数 _INLINE_VAR constexpr _Ph<3> _3{}; _INLINE_VAR constexpr _Ph<4> _4{}; .... _INLINE_VAR constexpr _Ph<20> _20{}; }
-
bind的基本使用:可以帮定普通函数也可以是成员函数
#include <iostream> #include <functional> using namespace std; // 普通函数 int add(int x, int y) { return x + y; } // 成员函数 class ADD { public: int add(int x, int y) { return x + y; } }; int main() { ADD obj; //绑定普通函数返回函数对象 auto f1 = bind(add, placeholders::_1, 2); //绑定成员函数返回函数对象 auto f2 = bind(&ADD::add, &obj,2, placeholders::_1); cout << f1(1) << endl; cout << f2(1) << endl; return 0; }
二、function函数对象
-
function是个模板类,可以对函数类型进行推演,底层调用实现的也是operator()()重载,不过调用的是封装的可调用实体目标,些目标实体包括:普通函数、lambda表达式、函数指针、函数对象、类的成员函数以及类的静态成员函数
-
bind
返回的函数对象 和lambda
匿名函数,一般只能作用一条语句中,function
可以用来保留他们的类型,在多条语句中使用 -
基本使用
#include<iostream> #include<functional> #include<string> using namespace std; void hello1() // 函数类型:void(),返回值是void,不带参数 { cout << "hello word" << endl; } class Test { public: //成员函数的函数指针,void(Test::*pfunc)(string) void hello(string str) { cout << str << endl; } }; int main() { // 通过function函数对象封装 普通函数hello1 function<void()> func1 = hello1; func1(); // 等价于func2.operator() => hello1() // 封装 函数对象(lambda表达式本质就是函数对象) function<int(int,int) fun2 = [](int a,int b)->int{return a+b;}; cout<< func2(100,200)<<endl; // 封装 类成员函数 function<void(Test&&,string)> fun3 = &Test::hello; func3(Test(),"hello"); return 0; }
-
function底层原理:
void hello(string str){cout << str << endl;} int sumA(int a, int b) { return a + b; } //typename... A 可变参,表示A不是一个类型而是一组类型 template<typename R, typename... A> //表示部分特例化 ,对有一个返回值,形参个数不全的函数进行特例化 class myfunction<R(A...)> { public: using PFUNC = R(*)(A...); //定义函数指针 myfunction(PFUNC pfunc) :_pfunc(pfunc) {} //给函数指针赋值 //arg 表示的是一组参数 A...可变参类型, 可以表示很多类型 R operator()(A... arg) { return _pfunc(arg...); } private: PFUNC _pfunc; }; int main() { myfunction<void(string)> func1(hello); //helloworld! => func1.operator("helloworld!"); func1("helloworld!"); myfunction<int(int,int)> func2(sumA); cout << func2(20, 20) << endl; //40 return 0; }
三、lambda表达式
-
lambda
表达式的底层实现就是函数对象
,创建的是匿名函数对象,相比于函数对象(必须定义一个类)更加灵活 -
函数对象:
class add { public: add(){} //构造函数,用于捕获外部变量 /* (int a,int b) 形参列表, int返回值类型 */ int operator()(int a,int b){/*函数体*/} };
-
lambda表达式的基本语法:
[捕获外部变量](形参列表)->返回值 {函数体};
->返回值类型
:- 相当于函数对象
operator()()
重载的返回值类型 - 没有返回值,也就是返回值
是void
,则->返回值类型
可以省略
- 相当于函数对象
(形参列表)
:- 相当于函数对象
operator()()
重载的形参列表
- 相当于函数对象
{函数体}
:- 相当于函数对象
operator()()
重载中的函数体
- 相当于函数对象
[捕获外部变量]
:- 相当于是函数对象的构造函数
[]
=> 不捕获外部变量,匿名函数没有成员变量[=]
=>以值传递的方式捕获外部所有变量[&]
=>以引用的方式捕获外部所有变量[this]
=> 捕获外部this指针,用于类中使用[=,&a]
=>以值传递的方式捕获外部所有变量,对变量a采用引用的方式捕获[a,b]
=> 以值传递的方式捕获外部变量a,b[a,&b]
=>以值传递的方式捕获外部变量a,以引用的方式捕获变量b
-
lambda匿名函数的底层实现(简版)
-
前置代码
#include<iostream> using namespace std; int main() { int a=10; int b=20; // 裸lambda auto fu = [](){}; // 带返回值 auto fun = []()->int{}; //带参数列表、返回值、函数体 auto func = [](int a,int b)->int{return a+b;}; //值传递外部捕获、参数列表、返回值、函数体 auto func1 = [=](int c,int d)->int{int f =a;return c+d;}; //值传递、mutable、函数体 auto func2 = [=]()mutable{int tmp = a;a=b;b=tmp ;}; //引用传递、函数体 auto func3 = [&]() {int tmp = a; a = b; b = tmp; }; return 0; }
-
什么都没有的裸匿名函数对象:
-
auto fu = [](){};
-
底层实现:
class __lambda_8_12 { public: // 返回值位空,没有函数体和形参列表 inline void operator()() const{} }; _lambda_8_12 fu = __lambda_8_12(__lambda_8_12{});
-
-
只带返回值的匿名函数对象:
-
auto fun = []()->int{};
-
底层实现:
class __lambda_9_13 { public: //带返回值 inline int operator()() const{} //只读属性 }; __lambda_9_13 fun = __lambda_9_13(__lambda_9_13{});
-
-
带形参列表、返回值、函数体的匿名函数对象:
-
auto func = [](int a,int b)->int{return a+b;};
-
底层实现
class __lambda_10_14 { public: inline int operator()(int a, int b) const //只读属性 { return a + b; } }; __lambda_10_14 func = __lambda_10_14(__lambda_10_14{});
-
-
带值传递、形参列表、返回值、函数体的匿名函数对象:
-
auto func1 = [=](int c,int d)->int{int f =a;return c+d;};
-
底层实现:
class __lambda_11_15 { public: inline int operator()(int c, int d) const //只读属性 { int f = a; return c + d; } private: int a; __lambda_11_15(int & _a): a{_a} {} //以传传递捕获外部变量 }; __lambda_11_15 func1 = __lambda_11_15(__lambda_11_15{a});
-
-
带值传递、形参列表、函数体、mutable关键字的匿名函数对象:
-
auto func2 = [=]()mutable{int tmp = a;a=b;b=tmp ;};
-
底层实现:
class __lambda_12_15 { public: inline void operator()() //可读可写 { int tmp = a; a = b; b = tmp; } private: int a; int b; public: __lambda_12_15(int & _a, int & _b): a{_a}, b{_b} {} }; __lambda_12_15 func2 = __lambda_12_15(__lambda_12_15{a, b});
-
-
带引用传递、函数体的匿名函数对象:
-
auto func3 = [&]() {int tmp = a; a = b; b = tmp; };
-
底层实现:
class __lambda_13_15 { public: inline void operator()() const //只读属性 { int tmp = a; a = b; b = tmp; } private: int & a; //引用接收外部变量 int & b; public: __lambda_13_15(int & _a, int & _b): a{_a}, b{_b}{} }; __lambda_13_15 func3 = __lambda_13_15(__lambda_13_15{a, b});
-
-
TIP:
- 不带
mutable
关键的lambda
匿名函数对象中的operator()()
重载是只读属性的(const),不能修改从外部捕获的成员变量 - 只要选择外部引入方式,且在函数体内部使用了这个变量,才会生成对应的成员变量
- 不带
-