广东哪里有网站建设,腾讯云市场 wordpress,网站制作设计收费,毕设做网站怎么样文章目录 1. function包装器1.1 遇到的问题1.2 包装器的定义1.3 解决问题1.4 包装器的其他应用 2. bind2.1 bind的定义2.2 bind包装器绑定固定参数2.3 bind包装器调整传参顺序2.4 bind包装器的意义 1. function包装器
1.1 遇到的问题
我们首先来看一行代码#xff1a;
ret … 文章目录 1. function包装器1.1 遇到的问题1.2 包装器的定义1.3 解决问题1.4 包装器的其他应用 2. bind2.1 bind的定义2.2 bind包装器绑定固定参数2.3 bind包装器调整传参顺序2.4 bind包装器的意义 1. function包装器
1.1 遇到的问题
我们首先来看一行代码
ret func(x);假设这行代码能够正常运行那么这个func是什么呢函数名 函数指针 函数对象 lambda表达式对象很多种可能性这些都是可调用的类型。这么多的类型可能会导致模板的效率低下。
templateclass 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;}
};
void Test1()
{cout useF(f, 11.11) endl;//函数名cout useF(Functor(), 11.11) endl;//函数对象cout useF([](double d)-double {return d / 4; }, 11.11) endl;//lambda表达式
}可以看到这里的useF函数模板被实例化了三份。但是使用function包装器就可以让只被实例化一份出来。 1.2 包装器的定义
function包装器也叫做适配器。C中的function本质是一个类模板。 模板参数说明
Ret被包装的可调用对象的返回值类型Args…可调用对象的参数包
如何使用包装器呢
int func(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;}int plusd(int a, int b){return a b;}
};
void Test2()
{//函数名函数指针functionint(int, int) func1 func;cout func1(1, 2) endl;//函数对象仿函数functionint(int, int) func2 Functor();cout func2(1, 2) endl;//lambda表达式functionint(int, int) func3 [](const int a, const int b)-int {return a b; };cout func3(1, 2) endl;//类的静态成员函数functionint(int, int) func4 Plus::plusi;//这里可以不加取地址符号cout func4(1, 2) endl;//类的成员函数functionint(Plus, int, int) func5 Plus::plusd;//这里必须要加取地址符号并且声明的时候需要显示的传类名cout func5(Plus(), 1, 2) endl;//调用的时候需要传对象
}1.3 解决问题
现在我们知道了包装器的用法了那么怎么解决最开始的问题呢
使用包装器包装可调用对象然后将可调用对象传给useF
void Test3()
{functiondouble(double) func1 f;functiondouble(double) func2 Functor1();functiondouble(double) func3 [](double d)-double {return d / 4; };cout useF(func1, 11.11) endl;cout useF(func2, 11.11) endl;cout useF(func3, 11.11) endl;
}可以看到这里的useF函数模板只实例化出了一个对象。
1.4 包装器的其他应用
我们之前做过一道题逆波兰表达式求解150. 逆波兰表达式求值 - 力扣LeetCode当时使用的方法代码如下
class Solution {
public:int evalRPN(vectorstring tokens) {stackint st;for(auto str : tokens){if(str || str - || str * || str /){int right st.top();st.pop();int left st.top();st.pop();switch(str[0]){case :st.push(left right);break;case -:st.push(left - right);break;case *:st.push(left * right);break;case /:st.push(left / right);break;}}else//遇到数字{st.push(stoi(str));}}return st.top();}
};在这里我们使用switch语句来判断运算类型还需要在前面的条件里面枚举出运算类型非常麻烦如果在工程中这段代码的可维护性就非常差需要在if语句中增加枚举在switch语句中增加case语句这种情况就可以使用包装器来简化代码
首先建立运算符和执行函数之间的映射关系当需要某一运算的时候就直接通过运算符找到对应的可调用对象调用即可。、当运算类型增加时只需要增加运算符和执行函数之间的映射关系即可
那么修改后的代码如下
class Solution {
public:int evalRPN(vectorstring tokens) {stackint st;mapstring, functionint(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(auto str : tokens){if(opFuncMap.count(str) 0){st.push(stoi(str));}else{int right st.top();st.pop();int left st.top();st.pop();st.push(opFuncMap[str](left, right));}}return st.top();}
};2. bind
2.1 bind的定义
**bind时一个函数模板就像是一个函数包装器适配器接受一个可调用对象生成一个新的可调用对象来”适应“原对象的参数列表。**一般而言我们用它可以把一个原本接收N个参数的函数fn通过绑定一些参数返回一个接收M个M可以大于N但这么做没什么意义参数的新函数。同时使用bind函数还可以实现参数顺序调整等操作。 参数列表说明
Fn可调用对象Ret可调用对象的返回类型Args要绑定的参数列表值或者时占位符
可以将bind函数看作是一个通用的函数适配器它接受一个可调用对象生成一个新的可调用对象来“适应”原对象的参数列表。调用bind的一般形式auto newCallable bind(callable,arg_list) newCallable生成的新的可调用对象 callable:需要包装的可调用对象 arg_list逗号分隔的参数列表对应给定的callable的参数当调用newCallable的时候newCallable会调用callable并传给他arg_list中的参数。
一般使用bind调整参数位置的时候会使用一个类placeholders这是一个占位符类。 表示newCallable的参数它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对象中参数的位置比如_1为newCallable的第一个参数_2为第二个参数以此类推。
此外除了用auto接收包装后的可调用对象也可以用function类型指明返回值和形参类型后接收包装后的可调用对象。
2.2 bind包装器绑定固定参数
首先我们来看一种无意义的绑定
int Sub(int a, int b)
{return a - b;
}
void Test4()
{functionint(int, int) func1 bind(Sub, placeholders::_1, placeholders::_2);cout func1(1, 3) Sub(1, 3) endl;
}绑定时第一个参数传入函数指针这个可调用对象但后续传入的要绑定的参数列表依次是placeholders::_1和placeholders::_2表示后续调用新生成的可调用对象时传入的第一个参数传给placeholders::_1传入的第二个参数传给placeholders::_2。此时绑定后生成的新的可调用对象的传参方式和原来没有绑定的可调用对象是一样的所以说这是一个无意义的绑定。
如果想让Sub的第二个参数固定绑定为10就可以将绑定时的参数列表的palceholder::_2设置为10.
int Sub(int a, int b)
{return a - b;
}
void Test4()
{functionint(int) func2 bind(Sub, placeholders::_1, 10);cout func2(2) endl;
}此时调用绑定后新生成的可调用对象时就只需要传入一个参数它会将该值减10后的结果进行返回。
2.3 bind包装器调整传参顺序
同样的对于上面的Sub函数的例子我们想让传入的参数顺序进行调换也可以使用bind实现。将placeholder::_1和placeholder::_2的位置进行调换即可。
void Test5()
{functionint(int, int) func bind(Sub, placeholders::_2, placeholders::_1);cout func(1, 2) endl;
}根本原因就是因为后续调用新生成的可调用对象时传入的第一个参数会传给placeholders::_1传入的第二个参数会传给placeholders::_2因此可以在绑定时通过控制placeholders::_n的位置来控制第n个参数的传递位置。
2.4 bind包装器的意义
将一个函数的某些参数绑定为固定的值让我们在调用时可以不用传递某些参数。可以对函数参数的顺序进行灵活调整。 本节完…