目录
function包装器
function包装器也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。那么我们来看看,我们为什么需要function呢?
ret = func(x);
//上面func可能是什么呢?那么func可能是函数名?函数指针?函数对象(仿函数对象)?也有可能是
//lambda表达式对象?所以这些都是可调用的类型!如此丰富的类型,可能会导致模板的效率低下!
//为什么呢?我们继续往下看
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;
//lambda表达式
cout << useF([](double d)->double {return d / 4; }, 11.11) << endl;
return 0;
}
通过上面的程序验证,我们会发现useF函数模板实例化了三份。
包装器可以很好的解决上面的问题
std::function在头文件<functional>
//类模板原型如下
template <class T> function; //undefined
template<class Ret, class...Args>
class function<Ret(Args...)>;
//模板参数说明:
//Ret:被调用函数的返回类型
//Args...:被调用函数的形参
//使用方法如下:
#include<functional>
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()
{
//函数名(函数指针)
std::function<int(int, int)> func1 = f;
cout << func1(1, 2) << endl;
//函数对象
std::function<int(int, int)> func2 = Functor();
cout << func2(1, 2) << endl;
//lambda表达式
std::function<int(int, int)> func3 = [](const int a, const int b) {return a + b; };
cout << func3(1, 2) << endl;
//类的成员函数
std::function<int(int, int)>func4 = &Plus::plusi;
cout << func4(1, 2) << endl;
std::function<double(Plus, double, double)> func5 = &Plus::plusd;
cout << func5(Plus(), 1.1, 2.2) << endl;
return 0;
}
有了包装器,如何解决模板的效率低下,实例化多份的问题?
#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;
//lambda表达式
std::function<double(double)> func3 = [](double d)->double {return d / 4; };
cout << useF(func3, 11.11) << endl;
return 0;
}
包装器的其他一些场景:
150. 逆波兰表达式求值 - 力扣(LeetCode)
class Solution
{
public:
int evalRPN(vector<string>& tokens)
{
stack<int> 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();
}
};
//使用包装器以后的玩法
class Solution
{
public:
int evalRPN(vector<string>& tokens)
{
stack<int> st;
map<string, function<int(int, int)>> opFuncMap =
{
{"+",[](int i,int j) {return i + j; }},
{ "-",[](int i,int j) {return i - j; } },
{ "*",[](int i,int j) {return i * j; } },
{ "/",[](int i,int j) {return i / j; } }
};
for (auto& str : tokens)
{
if (opFuncMap.find(str) != opFuncMap.end())
{
int right = st.top();
st.pop();
int left = st.top();
st.pop();
st.push(opFuncMap[str](left, right));
}
else
{
st.push(stoi(str));
}
}
return st.top();
}
};
bind
std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接收一个可调用对象(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为第二个参数,以此类推。
//使用举例
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的第一,二个参数指定
function<int(int, int)> func1 = bind(Plus, placeholders::_1, placeholders::_2);
//auto func1 = bind(Plus,placeholders::_1, placeholders::_2);
//func2的类型为function<void(int,int,int)>与func1类型一样
//表示绑定函数plus的第一,二为:1,2
auto func2 = bind(Plus, 1, 2);
cout << func1(1, 2) << endl;
cout << func2() << endl;
Sub s;
//绑定成员函数
function<int(int, int)> func3 = bind(&Sub::sub, s, placeholders::_1, placeholders::_2);
//参数调换顺序
function<int(int, int)> func4 = bind(&Sub::sub, s, placeholders::_2, placeholders::_1);
cout << func3(1, 2) << endl;
cout << func4(1, 2) << endl;
return 0;
}