文章目录
- 前言
- 第一种
- 第二种 仿函数
- 第三种 lambda表达式
- 一、Function包装器
- 二、使用场景
- 三、std::bind
前言
到目前为止的学习,我们知晓了三种方式来传函数。
第一种
#include<iostream>
int Plus(int i, int j)
{
return i + j;
}
int main()
{
int(*func)(int, int) = Plus;
std::cout << func(4, 5) << std::endl;
}
这种方式是C语言就支持的写法,但是实在繁琐,且没有一定C语言功底不易理解。
第二种 仿函数
class Func {
public:
Func(){} //无参构造函数
int operator()(int i, int j)
{
return i + j;
}
private:
};
int main()
{
std::cout << Func()(1, 2) << std::endl;;
Func f1;
std::cout << f1(4, 5) << std::endl;;
return 0;
}
很多容器例如map,set类就可以传仿函数进去。
第三种 lambda表达式
void Plus(int i, int j)
{
std::cout << i + j << std::endl;
}
int main()
{
std::thread t1([](int i, int j) {std::cout << i + j<< std::endl; },1 ,2);
std::thread t2(Plus,1 ,2);
t1.join();
t2.join();
return 0;
}
lambda的本质其实就是仿函数,但是lamda表达式有一个缺点就是不好确定类型,在一些例如map对象的定义时,模版的实例化不好确定类型。
提示:以下是本篇文章正文内容,下面案例可供参考
一、Function包装器
基于上面的三种不同的传函数方式,C++11提供Function来对函数进行一个整体的包装,将类型统一化,更加具有实用性和方便性。
需要包头文件 #include< functional >
int plus(int i, int j)
{
return i + j ;
}
class Func {
public:
Func(){} //无参构造函数
int operator()(int i, int j)
{
return i + j;
}
private:
};
class Plus {
public:
static int plus(int i, int j)
{
return i + j;
}
int Add(int i, int j)
{
return i + j;
}
};
int main()
{
//函数名
std::function<int(int, int)> f1(plus);
//仿函数对象
std::function<int(int, int)> f2 = Func();
//lambda表达式
std::function<int(int, int)> f3([](int i, int j) { return i + j; });
//类的静态成员函数
std::function<int(int, int)> f4(&Plus::plus);
//类的成员函数
//需要一个函数对象来调用
std::function<int(Plus, int, int)> f5(&Plus::Add);
std::cout << f1(1, 2) << std::endl;
std::cout << f2(2, 3) << std::endl;
std::cout << f3(3, 4) << std::endl;
std::cout << f4(5, 6) << std::endl;
std::cout << f5(Plus(),6, 7) << std::endl;
return 0;
}
二、使用场景
LeetCode_逆波兰表达式求值
力扣这一题就可以很好反映function的使用,需要使用到操作函数来解题。
我们如果要使用map来映射操作符号和对应函数,就可以使用function来包装。
class Solution {
public:
int evalRPN(vector<string>& tokens) {
std::stack<int> st;
std::map<string,function<int(int,int)>> func = {
{"+",[](int left, int right){return left + right;}},
{"-",[](int left, int right){return left - right;}},
{"*",[](int left, int right){return left * right;}},
{"/",[](int left, int right){return left / right;}} };
for(auto str: tokens)
{
if(func.find(str) != func.end())
{
int right = st.top();
st.pop();
int left = st.top();
st.pop();
st.push(func[str](left,right));
}
else
{
st.push(std::stoi(str));
}
}
return st.top();
}
};
三、std::bind
bind也可以理解为一个函数包装器,接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。
int minus(int i, int j)
{
return i - j;
}
int main()
{
//生成的新func1与plus一致
auto func1 = std::bind(minus, std::placeholders::_1, std::placeholders::_2);
std::cout << "func1: " << func1(5, 1) << " plus: " << minus(5, 1) << std::endl;
//生成的新func2参数调换
auto func2 = std::bind(minus, std::placeholders::_2, std::placeholders::_1);
std::cout << "func2: " << func2(5, 1) << " plus: " << minus(5, 1) << std::endl;
//生成的新func3,func4,func5参数数量改变
auto func3 = std::bind(minus, 5, std::placeholders::_1);
std::cout << "func2: " << func3(1) << " plus: " << minus(5, 1) << std::endl;
auto func4 = std::bind(minus, std::placeholders::_1, 1);
std::cout << "func2: " << func4(5) << " plus: " << minus(5, 1) << std::endl;
auto func5 = std::bind(minus, 5, 1);
std::cout << "func2: " << func5() << " plus: " << minus(5, 1) << std::endl;
return 0;
}
std::placeholders::_n 在这里作为“占位符”,代表newCallable的参数,它们占据了传递给newCallable的参数的“位置”,数值n表示生成的可调用对象中参数的位置:_1为newCallable的第一个参数,_2为第二个参数,以此类推。
从上面的例子中,我们可以看出bind不仅可以使新生成的可调用对象的参数顺序变化,还可以固定某个参数的值和改变参数个数。 但是看上去并不实用? 那么在哪里实用呢?
例如再上面我们讲的
//类的成员函数
//需要一个对象来调用
std::function<int(Plus, int, int)> f5(&Plus::Add);
这里使用function对类的成员函数进行包装,需要传一个对象才可以,与其它的函数包装还不一样,通过bind我们就可以这样传。
class Plus {
public:
int Add(int i, int j)
{
return i + j;
}
};
int main()
{
auto func1 = std::bind(&Plus::Add, Plus(), std::placeholders::_1, std::placeholders::_2);
std::cout << "func1: " << func1(1, 2) << " Plus::Add: " << Plus().Add(1, 2) << std::endl;
std::function<int(Plus, int, int)> f5(&Plus::Add);
std::function<int(int, int)> func2 = std::bind(f5, Plus(), std::placeholders::_1, std::placeholders::_2);
std::cout << "func2: " << func2(1, 2) << " f5: " << f5(Plus(), 1, 2) << std::endl;
return 0;
}
这种场景下,我们就可以使用bind来创建一个新的可调用对象来少传一个临时对象。