前言:
上文我们学了C++11中一个新的表达式:Lambda表达式。Lambda表达式可以在函数内部定义,其本质是仿函数【C++11】Lambda表达式-CSDN博客
本文我们来学习C++11的下一个新语法:包装器
function
function的定义为:
template <class T>
class function; // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
function的一个类模板,也是一个包装器。function的实例对象可以包装调用一切可调用对象。但若function的对象为被初始化,进行调用就会报错(抛异常:std::bad_function_call)
function模板类放在<functional>头文件中
使用function包装不同的可调用对象,就可以用相同的方式调用不同可调用对象。
function的本质其实也是仿函数
使用细节如下:
#include<iostream>
#include<functional>
using namespace std;
//function可以包装一切可调用对象,其本质是仿函数
//template <class Ret, class... Args>
//class function<Ret(Args...)>;
int f(int a, int b)
{
return a + b;
}
struct Functor
{
int operator() (int a, int b)
{
return a - b;
}
};
class Plus
{
public:
Plus(int n = 10)
:_n(n)
{}
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return (a + b) * _n;
}
private:
int _n;
};
int main()
{
//可包装一切可调用对象
function<int(int, int)> a = f;
function<int(int, int)> b = Functor();
function<int(int, int)> c = [](int a, int b) {return a * 10 + b; };
cout << a(1, 2) << endl;
cout << b(1, 2) << endl;
cout << c(1, 2) << endl;
//既然说function可以包装一切可调用对象,那么类成员函数既然也可以被包装
//包装静态成员函数: 1.必须要指定类域,2.非静态成员必须要取地址,但是静态不用,为了同一格式建议加上
function<int(int, int)> x = &Plus::plusi;
cout << x(1, 2) << endl;
//包装非静态成员函数:1.必须要指定类域 2.必须取地址 3.非静态成员函数有this指针
function<int(Plus*,int, int)> y = &Plus::plusd;
Plus p;
cout << y(&p, 1, 2) << endl;
//也可以直接传对象,因为:类成员函数的调用是通过 .* 运算符实现的
//前面传指针,也是先将指针解引用为对象,再通过 .* 运算符调用成员函数
function<int(Plus, int, int)> z = &Plus::plusd;
cout << z(p, 1, 2) << endl;
//与直接传对象类似,引用是为了减少拷贝,提高效率
function<int(Plus&&, int, int)> q = &Plus::plusd;
cout << q(move(p), 1, 2) << endl;
function<int(Plus&, int, int)> w = &Plus::plusd;
cout << w(p, 1, 2) << endl;
}
包装其他可调用对象,没什么区别,唯独需要注意当我们在包装类成员函数时有一些不同:
包装静态成员函数时:1.必须要指定类域,2.非静态成员必须要取地址,但是静态不用,为了统一格式建议加上。
包装非静态成员函数时:1.必须要指定类域 2.必须取地址 3.非静态成员函数有this指针。
练习:150. 逆波兰表达式求值 - 力扣(LeetCode)
#include<iostream>
#include<vector>
#include<functional>
#include<map>
#include<stack>
#include<string>
using namespace std;
class Solution
{
public:
int evalRPN(vector<string>& tokens)
{
stack<int> s;
//使用map将运算符与运算逻辑匹配
//使用Lambda实现运算逻辑
//利用function包装Lambda
map<string, function<int(int, int)>> m =
{
{"+",[](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)
{
//当count返回不是0,说明当前的字符是"+ - * /"中的一个
//取两个操作数,进行相应的运算
if (m.count(e))
{
int left = s.top();
s.pop();
int right = s.top();
s.pop();
//operator[]返回function对象的引用
//function对象直接调用Lambda表达式
int num = m[e](right, left);
s.push(num);
}
else
{
s.push(stoi(e));//stoi函数:将字符串转化为整型
}
}
return s.top();
}
};
int main()
{
vector<string> arr = { "4","13","5","/","+" };
cout << Solution().evalRPN(arr);
}
bind
bind的定义:
simple(1)
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接收一个函数处理后会返回一个可调用对象。bind的功能:调整函数参数顺序、控制函数参数个数。
调用bind的一般形式为:auto newCallable = bind(callable,arg_list);callable是函数地址,newCallable是可调用对象,调用newCallable会将arg_list的参数传给callable,然后调用callable。bind的底层和function一样都是仿函数,所以也可以用function代替auto来接收bind的返回对象。
在arg_list中有一个特殊概念:占位符。假设有n个参数,那占位符至多有n个:_1 , _2 , _3 , ....... , _n。_1代表第一个参数,_2代表第二个参数,........ ,_n代表第n个参数。这些占位符放在placeholders命名空间。
使用样例:
#include<iostream>
#include<functional>
using namespace std;
//bind的占位符都放在这个命名空间里
using namespace placeholders;
//bind有两个作用:1.交换参数顺序 2.绑定参数,控制参数个数(常用)
//bind的本质和function一样都是仿函数
class Plus
{
public:
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return a + b;
}
};
int add(int a, int b)
{
return a - b;
}
int add1(int a, int b, int c)
{
return a * 10 + b - c;
}
int main()
{
//调整参数顺序(没什么用处)
auto e = bind(add, _1, _2);
cout<<e(2, 3)<<endl;
auto x = bind(add, _2, _1);
cout << x(2, 3) << endl;
//调整参数个数(常用)
//直接绑定第1、2、3的参数,传参的时候只用传未绑定的参数
auto c = bind(add1, 10, _1, _2);
cout << c(1,2) << endl;
auto d = bind(add1, _1, 10, _2);
cout << d(1, 2) << endl;
auto w = bind(add1, _1, _2, 10);
cout << w(1, 2) << endl;
//bind常用于绑定固定参数
//function<double(Plus*,double, double)> f = &Plus::plusd;
function<double(double, double)> f = bind(&Plus::plusd, Plus(), _1, _2);
cout<<f(1, 2)<<endl;
}
总的来看,bind有用的功能主要是控制函数参数个数,常用于绑定一些固定的参数。