包装器
function包装器
- function包装器也叫适配器,c++中的function本质是一个类模板,也是一个包装器,那么为什么需要包装器呢。
template<class F, class T>
T useF(F f, T x)
{
static int count = 0;
cout << "count:" << ++count << endl;
cout << "count:" << &count << endl;
return f(x);
}
double f(double i)
{
return i / 2;
}
struct Functor
{
double operator()(double d)
{
return d / 3;
}
};
int main()
{
// 函数名
cout << useF(f, 11.11) << endl;
// 函数对象
cout << useF(Functor(), 11.11) << endl;
// lamber表达式
cout << useF([](double d)->double{ return d/4; }, 11.11) << endl;
return 0;
}
运行结果:
candy:~/code/cpp/test/tuple $ ./a.out
count:1
count:0x5624be36c15c
5.555
count:1
count:0x5624be36c160
3.70333
count:1
count:0x5624be36c158
2.7775
对于一个ret = func(x),我们并不知道func是什么,func可能是函数指针,也可能是函数名,仿函数,lambda表达式,所以这些都是可调用的类型,这样就会导致模板的效率低下,就如同我刚写的代码,count的地址是不一样的,这就是说函数模板实例化出来了多份,而包装器就可以很好的解决这个问题。
function说明
std::function在头文件<functional>
// 类模板原型如下
template <class T> function; // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参
function的使用
int f(int a, int b)
{
return a + b;
}
struct Functor
{
public:
int operator() (int a, int b)
{
return a + b;
}
};
class Plus
{
public:
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return a + b;
}
};
int main(){
//绑定普通函数
function<int(int, int)> f1 = f;
cout << f1(2,3) << endl;
//绑定仿函数
function<int(int,int)> f2 = Functor();
cout << f2(3,4) << endl;
//绑定lambda表达式
function<int(int,int)> f5 = [](int a, int b){ return a+b; };
cout << f5(323,3243) << endl;
//绑定类静态成员函数 -> 要指定类域
function<int(int,int)> f3 = Plus::plusi;
cout << f3(32,32) << endl;
//绑定非静态成员函数:
//1.首先非静态成员函数的函数指针要加上&符号
//2.因为非静态成员函数中还有一个隐藏的参数 -> this指针,所以要显示的传递一个类的对象或者指针
function<double(Plus,double,double)> f4 = &Plus::plusd;
Plus p1;
cout << f4(p1, 1.1,1.32) << endl;
return 0;
}
这里除了非静态成员变量的使用有两点要注意以外,其余的使用都比较简单,那么为什么function可以解决模板低效的问题呢 -> function 完成了对类型的统一
#include <functional>
template<class F, class T>
T useF(F f, T x)
{
static int count = 0;
cout << "count:" << ++count << endl;
cout << "count:" << &count << endl;
return f(x);
}
double f(double i)
{
return i / 2;
}
struct Functor
{
double operator()(double d)
{
return d / 3;
}
};
int main()
{
// 函数名
std::function<double(double)> func1 = f;
cout << useF(func1, 11.11) << endl;
// 函数对象
std::function<double(double)> func2 = Functor();
cout << useF(func2, 11.11) << endl;
// lamber表达式
std::function<double(double)> func3 = [](double d)->double{ return d / 4; };
cout << useF(func3, 11.11) << endl;
return 0;
}
运行结果:
candy:~/code/cpp/test/tuple $ g++ test.cc
candy:~/code/cpp/test/tuple $ ./a.out
count:1
count:0x564b0f7cb154
5.555
count:2
count:0x564b0f7cb154
3.70333
count:3
count:0x564b0f7cb154
2.7775
可以看到这里的count的地址都是相同的,这就统一了类型的问题,而function的好处还不止这些,试想一下如果要存储对应的string和函数的关系,我们要如何存储,在c++11之前或许需要使用函数指针来进行操作,但是函数指针相对来说阅读起来观感并不好,但是现在一个function就可以解决这个问题。
对于这样的一个题,我们就可以使用function来统一类型使用map来进行管理
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> v;
unordered_map<string, function<int(int,int)>> op = {
{"+", [](int a, int b){ return a + b;}},
{"-", [](int a, int b){ return a - b;}},
{"*", [](int a, int b){ return a * b;}},
{"/", [](int a, int b){ return a / b;}}
};
for(auto e : tokens){
if(op.count(e)){
int right = v.top();
v.pop();
int left = v.top();
v.pop();
v.push(op[e](left, right));
}
else{
v.push(stoi(e));
}
}
return v.top();
}
};
bind
- std::bind定义在functional头文件中,是一个函数模板,他就是一个函数包装器(适配器),接收一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。一般而言,我们用它可以把一个原来接收N个参数的函数fn,通过绑定一些参数,返回一个接收M(一般来说M<N)个参数的新函数, 同时使用std::bind还可以实现参数顺序的调整等操作。
// 原型如下:
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2)
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
可以将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对 象来“适应”原对象的参数列表。 调用bind的一般形式:auto newCallable = bind(callable,arg_list);
其中,newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的callable的参数。当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中 的参数。arg_list中的参数可能包含形如_n的名字,
其中_n是一个整数,这些参数是“占位符”,表示newCallable的参数,它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对 象中参数的位置:_1为newCallable的第一个参数,_2为第二个参数,以此类推。
eg:
// 使用举例
#include <functional>
int Plus(int a, int b)
{
return a + b;
}
class Sub
{
public:
int sub(int a, int b)
{
return a - b;
}
};
int main()
{
//表示绑定函数plus 参数分别由调用 func1 的第一,二个参数指定
std::function<int(int, int)> func1 = std::bind(Plus, placeholders::_1, placeholders::_2);
//auto func1 = std::bind(Plus, placeholders::_1, placeholders::_2);
//func2的类型为 function<void(int, int, int)> 与func1类型一样
//表示绑定函数 plus 的第一,二为: 1, 2
auto func2 = std::bind(Plus, 1, 2);
cout << func1(1, 2) << endl;
cout << func2() << endl;
Sub s;
// 绑定成员函数
std::function<int(int, int)> func3 = std::bind(&Sub::sub, s, placeholders::_1, placeholders::_2);
// 参数调换顺序
std::function<int(int, int)> func4 = std::bind(&Sub::sub,s, placeholders::_2,placeholders::_1);
cout << func3(1, 2) << endl;
cout << func4(1, 2) << endl;
return 0;
}
- 这里除了可以这样让非静态成员变量少传一个参数之外,还可以绑定一写必须要传的参数从而减少参数个数。