目录
1、function包装器
2、function包装器包装成员函数指针
2.1 静态成员函数
2.2 非静态成员函数
3、bind包装器
3.1 调整参数顺序
3.2 调整参数个数
1、function包装器
包装器是用来包装可调用对象的,这里的可调用对象主要有函数指针、仿函数、lambda表达式
function本质是一个类模板,也是一个包装器,头文件是<functional>
function<返回值(参数列表)> f = 可调用对象
int f(int a, int b)
{return a + b;
}
struct Functor
{
public:int operator() (int a, int b){return a + b;}
};
int main()
{// 包装可调用对象function<int(int, int)> f1 = f;function<int(int, int)> f2 = Functor();function<int(int, int)> f3 = [](int a, int b) {return a + b; };cout << f1(1, 2) << endl;cout << f2(1, 2) << endl;cout << f3(1, 2) << endl;return 0;
}
优点是可以进行类型统一,就像我们玩游戏使用英雄释放技能时,按下一个技能的按键,英雄就会做出相应的动作,实际上,按下按键救会产生一个命令,命令通常是一个字符串,一般会将命令和函数进行映射。为了映射,此时可以使用一个map来完成映射,第一个参数存命令,第二个参数存这个命令对应的函数,但函数可能是函数指针、仿函数、lambda,lambda没有类型(底层有,但是同一个lambda在不同时间生成的类型都不一样),仿函数只能写死,函数指针可以,但太麻烦,所以就可以考虑使用包装器来进行统一
我们通过一个题目来看
150. 逆波兰表达式求值 - 力扣(LeetCode)
这道题普通的做法是开一个栈,遍历数组,当遍历到的字符是数字时,就放入栈,当遍历到的字符不是数字时,就取栈顶的两个数来进行操作,并将操作完成的数再放入栈
class Solution {
public:bool isNumber(const string& s){return !(s == "+" || s == "-" || s == "*" || s == "/");}int evalRPN(vector<string>& tokens) {stack<int> st;for(const auto& e : tokens){if(isNumber(e)) st.push(stoi(e));else{int y = st.top();// 注意:这里是用x / yst.pop();int x = st.top();st.pop();switch(e[0])// 这里不能是e{case '+':st.push(x + y);break;case '-':st.push(x - y);break;case '*':st.push(x * y);break;case '/':st.push(x / y);break;}}}return st.top();}
};
现在可以使用一个map,第一个参数存放操作符,第二个参数存放对应的操作
class Solution {
public:bool isNumber(const string& s){return !(s == "+" || s == "-" || s == "*" || s == "/");}int evalRPN(vector<string>& tokens) {stack<int> st;map<string, function<int(int, int)>> opFuncMap = {{"+", [](int x, int y){return x + y;}},{"-", [](int x, int y){return x - y;}},{"*", [](int x, int y){return x * y;}},{"/", [](int x, int y){return x / y;}}};for(const auto& e : tokens){if(isNumber(e)) st.push(stoi(e));else{int y = st.top();// 注意:这里是用x / yst.pop();int x = st.top();st.pop();int ret = opFuncMap[e](x, y);st.push(ret);}}return st.top();}
};
2、function包装器包装成员函数指针
2.1 静态成员函数
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 = Plus::plusi; // 要指定类域cout << f1(1, 1) << endl;return 0;
}
静态成员函数与普通函数是类似的,只是需要指定类域
2.2 非静态成员函数
int main()
{// 包装静态成员函数function<int(int, int)> f1 = Plus::plusi; // 要指定类域cout << f1(1, 1) << endl;// 包装非静态成员函数function<double(double, double)> f2 = Plus::plusd;return 0;
}
此时是错误的,非静态成员函数要取函数指针需要加&,静态成员函数可加可不加,最好也加上
int main()
{// 包装静态成员函数function<int(int, int)> f1 = &Plus::plusi; // 要指定类域cout << f1(1, 1) << endl;// 包装非静态成员函数function<double(double, double)> f2 = &Plus::plusd;return 0;
}
此时还是不行的,因为非静态成员函数还要隐藏的this指针,可以传指针,也可传对象(有名对象或匿名对象)
int main()
{// 包装静态成员函数function<int(int, int)> f1 = &Plus::plusi; // 要指定类域cout << f1(1, 1) << endl;// 包装非静态成员函数Plus ps;function<double(Plus*, double, double)> f2 = &Plus::plusd;cout << f2(&ps, 7.7, 7.7) << endl;// 包装非静态成员函数function<double(Plus, double, double)> f3 = &Plus::plusd;cout << f3(Plus(), 7.7, 7.7) << endl;return 0;
}
此时就会有一个疑问,非静态成员函数多出来的参数是this指针,是一个指针,为什么可以传对象呢?
实际上,function本质并不是将3个参数传给plusd,是拿到函数指针后,将函数指针作为成员变量存起来,我们传参时实际上是调用了function的operator(),operator()又去调用plusd,而调用plusd就需要用到Plus的对象或指针。也就是说,不是直接将3个参数传给plusd,并且this指针也不支持显示传参
3、bind包装器
bind是一个函数模板,主要用于对可调用对象调整参数的顺序、个数,返回一个仿函数,直接使用auto接收
placeholders是一个命名空间,里面有很多标识符(_1,_2,_3,...),可用这些标识符去代表对应的参数,_1始终代表第一个实参,_2代表第二个实参
3.1 调整参数顺序
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;
int Sub(int x, int y)
{return (x - y) * 10;
}
int main()
{// 调整参数顺序auto sub1 = bind(Sub, _1, _2);cout << sub1(10, 5) << endl;auto sub2 = bind(Sub, _2, _1);cout << sub2(10, 5) << endl;return 0;
}
结果是50 -50。_1代表的始终是第1个实参,无论是sub1(10, 5),还是sub2(10, 5),第1个实参都是10,都会匹配到_1,但是sub1和sub2中_1处在的位置是不同的,sub1会将_1当成第1个形参来传,sub2会将_1当成是第2个形参来传,也就是说在调用函数时,是按顺序传的
3.2 调整参数个数
int main()
{// 调整参数顺序auto sub1 = bind(Sub, _1, _2);cout << sub1(10, 5) << endl;auto sub2 = bind(Sub, _2, _1);cout << sub2(10, 5) << endl;// 调整参数个数auto sub3 = bind(Sub, 100, _1);cout << sub3(5) << endl;auto sub4 = bind(Sub, _1, 100);cout << sub4(5) << endl;return 0;
}
结果是950 -950
实际上,bind返回的是一个仿函数对象,也就是说上面的sub1、sub2、sub3、sub4都是仿函数对象,本质也是调用operator(),只是里面调用的顺序不同
其实,bind主要用于绑定一些固定参数,向上面非静态成员函数的传参每次都需要传指针或对象,可用直接绑定了,这样只需要传操作数
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<double(double, double)> f1 = bind(&Plus::plusd, Plus(), _1, _2);cout << f1(7.7, 7.7) << endl;return 0;
}