1.function包装器
在C++11中,可以使用std::function
来创建函数包装器。std::function
是一个通用的函数对象,可以存储、复制和调用各种可调用对象,包括函数指针、函数对象、Lambda表达式等。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;
// lamber表达式
cout << useF([](double d) -> double { return d / 4; }, 11.11) << endl;
return 0;
}
输出结果如下:
count:1
count:00EDC140
5.555
count:1
count:00EDC144
3.70333
count:1
count:00EDC148
2.7775
可以发现,三个count的地址都不同,因此可以发现useF
函数模板实例化了三份。
function使用方式
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;
}
};
1. 函数指针
// 函数名(函数指针)
std::function<int(int, int)> func1 = f;
cout << func1(1, 2) << endl;
在这个示例中,我们将函数f
的指针传递给std::function
的构造函数来创建一个函数包装器func1
。由于f
是一个函数指针,它的类型与std::function<int(int, int)>
匹配,它接受两个int
类型的参数并返回一个int
类型的值。我们可以通过func1
来调用包装的函数,并传递参数。
2. 函数对象
// 函数对象
std::function<int(int, int)> func2 = Functor();
cout << func2(1, 2) << endl;
在这个示例中,我们创建了一个函数对象Functor
的实例,并将其传递给std::function
的构造函数来创建函数包装器func2
。由于Functor
是一个可调用的对象(定义了operator()
函数),它的类型与std::function<int(int, int)>
匹配。我们可以通过func2
来调用包装的函数对象,并传递参数。
3. Lambda表达式
// Lambda表达式
std::function<int(int, int)> func3 = [](const int a, const int b) { return a + b; };
cout << func3(1, 2) << endl;
在这个示例中,我们使用Lambda表达式创建了一个匿名函数,并将其传递给std::function
的构造函数来创建函数包装器func3
。Lambda表达式的类型由编译器推导出来,并与std::function<int(int, int)>
匹配。我们可以通过func3
来调用包装的Lambda函数,并传递参数。
4. 类的静态成员函数
// 类的静态成员函数
std::function<int(int, int)> func4 = &Plus::plusi;
cout << func4(1, 2) << endl;
在这个示例中,我们使用&Plus::plusi
来获取Plus
类的静态成员函数plusi
的指针,并将其传递给std::function
的构造函数来创建函数包装器func4
。静态成员函数不依赖于类的实例,因此不需要额外的对象参数。我们可以通过func4
来调用包装的静态成员函数,并传递参数。
5. 类的普通成员函数
//类的普通成员函数
std::function<double(Plus, double, double)> func5 = &Plus::plusd;
cout << func5(Plus(), 1.1, 2.2) << endl;
在这个示例中,我们使用&Plus::plusd
来获取Plus
类的普通成员函数plusd
的指针,并将其传递给std::function
的构造函数来创建函数包装器func5
。普通成员函数依赖于类的实例,因此我们需要额外传递一个类对象作为参数。我们可以通过func5
来调用包装的普通成员函数,并传递类对象和其他参数。
有了包装器,如何解决模板的效率低下,实例化多份的问题呢?
#include <functional>
#include <iostream>
using namespace std;
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;
}
输出结果:
count:1
count:0071E4C8
5.555
count:2
count:0071E4C8
3.70333
count:3
count:0071E4C8
2.7775
结果可以发现三个count地址都一样,通过function
只实例化出了一份。
包装器的其他一些场景:
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 {
// 1、atoi itoa
// 2、sprintf scanf
// 3、stoi to_string C++11
st.push(stoi(str));
}
}
return st.top();
}
};
使用包装器的解法:
class Solution {
public:
int evalRPN(vector<string> &tokens) {
stack<long> st;
unordered_map<string, function<long(long, long)>> opFuncMap = {
{"+", [](long x, long y) -> long { return x + y; }},
{"-", [](long x, long y) -> long { return x - y; }},
{"*", [](long x, long y) -> long { return x * y; }},
{"/", [](long x, long y) -> long { return x / y; }}};
for (auto &str: tokens) {
if (opFuncMap.find(str) != opFuncMap.end()) {
long right = st.top();
st.pop();
long left = st.top();
st.pop();
st.push(opFuncMap[str](left, right));
} else {
st.push(stoll(str));
}
}
return st.top();
}
};
2.bind
std::bind
是C++标准库中的一个函数模板,位于<functional>
头文件中。它用于创建函数对象(也称为函数绑定器),可以将一个可调用对象与其参数绑定起来,形成一个新的可调用对象。
原型如下:
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为第二个参数,以此类推。
1. 绑定函数指针
#include <functional>
#include <iostream>
void foo(int a, int b) {
std::cout << "Sum: " << (a + b) << std::endl;
}
int main() {
auto boundFunc = std::bind(foo, 10, 20);
boundFunc(); // 调用绑定的函数指针
return 0;
}
std::bind
将函数指针foo
与参数10
和20
绑定起来,创建了一个新的函数对象boundFunc
。调用boundFunc()
会调用原始函数foo
,并传递绑定的参数。输出结果为"Sum: 30"。
2. 绑定成员函数
#include <functional>
#include <iostream>
class MyClass {
public:
void printSum(int a, int b) {
std::cout << "Sum: " << (a + b) << std::endl;
}
};
int main() {
MyClass obj;
auto boundFunc = std::bind(&MyClass::printSum, &obj, 10, 20);
boundFunc(); // 调用绑定的成员函数
return 0;
}
std::bind
将成员函数printSum
与对象指针&obj
和参数10
、20
绑定起来,创建了一个新的函数对象boundFunc
。调用boundFunc()
会调用原始的成员函数printSum
,并传递绑定的对象和参数。输出结果为"Sum: 30"。
3. 绑定成员函数和占位符
#include <functional>
#include <iostream>
class MyClass {
public:
void printSum(int a, int b) {
std::cout << "Sum: " << (a + b) << std::endl;
}
};
int main() {
MyClass obj;
auto boundFunc = std::bind(&MyClass::printSum, &obj, std::placeholders::_1, 20);
boundFunc(10); // 调用绑定的成员函数,传递部分参数
return 0;
}
std::bind
将成员函数printSum
与对象指针&obj
和一个占位符std::placeholders::_1
(表示第一个参数)以及常数20
绑定起来,创建了一个新的函数对象boundFunc
。调用boundFunc(10)
会调用原始的成员函数printSum
,传递10
作为第一个参数和绑定的常数20
作为第二个参数。输出结果为"Sum: 30"。
4. 参数重排和缺省参数
#include <functional>
#include <iostream>
void foo(int a, int b, int c) {
std::cout << "Sum: " << (a + b * c) << std::endl; //230
}
int main() {
auto boundFunc = std::bind(foo, std::placeholders::_3, std::placeholders::_1, std::placeholders::_2);
boundFunc(10, 20, 30); // 调用绑定的函数,重排参数顺序
return 0;
}
std::bind
将函数foo
与占位符std::placeholders::_3
、std::placeholders::_1
和std::placeholders::_2
绑定起来,创建了一个新的函数对象boundFunc
。调用boundFunc(10, 20, 30)
会调用原始的函数foo
,参数按照重排的顺序传递,a绑定第三个参数30,b绑定第1个参数10,c绑定第二个参数20。即30+10*20 = 230。
std::bind
还支持对绑定的参数进行缺省设置,例如std::bind(foo, _1, _2, 100)
,其中100
是一个固定的常数作为缺省参数。文章来源:https://www.toymoban.com/news/detail-498875.html
function和bind配合使用
使用举例文章来源地址https://www.toymoban.com/news/detail-498875.html
#include <functional>
#include <iostream>
#include <map>
#include <string>
using namespace std;
using namespace placeholders;
int Plus(int a, int b) {
return a + b;
}
int Mul(int a, int b, int c, double rate) {
return a + b + c * rate;
}
class Sub {
public:
int sub(int a, int b) {
return a - b;
}
};
int main() {
// 调整顺序 -- 鸡肋
// _1 _2.... 定义在placeholders命名空间中,代表绑定函数对象的形参,
// _1,_2...分别代表第一个形参、第二个形参...
function<int(int, int)> funcPlus = Plus;
//function<int(Sub, int, int)> funcSub = &Sub::sub;
function<int(int, int)> funcSub = bind(&Sub::sub, Sub(), _2, _1);
function<int(int, int, int)> funcMul = bind(Mul, _3, _1, _2, 1.5);
cout << funcPlus(1, 2) << endl; //3
cout << funcSub(1, 2) << endl; //1
cout << funcMul(3, 2, 1) << endl;//a = 1, b = 3 c = 2 * 1.5 1+3+2*1.5=7
return 0;
}
int main() {
map<string, function<int(int, int)>> opFuncMap = {
{"+", Plus},
{"-", bind(&Sub::sub, Sub(), _1, _2)},
{"*", bind(Mul, _2, _1, 1, 2)},//这里只能有两个占位符,function的参数只有2个
};
cout << opFuncMap["+"](1, 2) << endl;//3
cout << opFuncMap["-"](1, 2) << endl;//-1
cout << opFuncMap["*"](2, 3) << endl;//2+3+1*2=7
}
到了这里,关于C++11 包装器function和bind的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!