c++进阶之----c++11(包装器)
1. <functional>
头文件
<functional>
是C++11标准库中提供的一个头文件,它包含了许多用于处理可调用对象的工具,例如函数包装器、绑定器、函数适配器等。这些工具可以帮助我们更方便地操作函数、lambda表达式、函数对象等。
(1)std::function
std::function
是一个通用的函数包装器,它可以存储、复制和调用任何可调用对象,包括普通函数、lambda表达式、函数对象、绑定表达式等。若 std::function 不含目标,则称它为空。调用空 std::function 的目标导致抛出 std::bad_function_call 异常。
特点:
-
类型安全:
std::function
通过模板参数指定其调用签名,确保类型安全。 -
通用性:可以包装几乎任何类型的可调用对象。
-
可复制性:可以像普通对象一样被复制和赋值。
有一点值得注意的是:(结合下述代码理解)
对于 std::function<int(int, int)>
这种形式,它表示 std::function
将包装一个接受两个 int
参数并返回一个 int
的可调用对象。这里的 int(int, int)
是一个函数类型,而不是两个类型。
如果尝试使用 std::function<int, int>
这种形式,编译器会报错,因为它不符合 std::function
模板的预期参数形式。std::function
的模板参数必须是函数类型,而不是两个独立的类型。
个人理解就是将两个int类型的参数给包装在一起,之后返回一个Int类型的结果(如有不对欢迎纠正)
(2)代码测试:
#include<iostream>
#include<functional>
using namespace std;
//函数
int f(int a, int b)
{
return a + b;
}
//仿函数
struct functor
{
public:
int operator()(int a, int b)
{
return a + b;
}
};
class Plus
{
public:
Plus(int n=9)
:_n(n)
{}
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return (a + b) * _n;
}
private:
int _n;
};
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; };//包装lambda表达式
cout << f1(1, 1) << endl;
cout << f2(1, 1) << endl;
cout << f3(1, 1) << endl;
// 包装静态成员函数
// 成员函数要指定类域并且前面加&才能获取地址
function<int(int, int)> f4 =&Plus::plusi;
cout << f4(1, 1) << endl;
function<double(Plus*, double, double)> f5 = &Plus::plusd;
Plus ps;
//对比一下:
//Plus ps; // 创建一个左值对象
//Plus(); // 创建一个右值临时对象
cout << f5(&ps, 1.1, 1.1) << endl;
function<double(Plus&, double, double)> f6 = &Plus::plusd;
cout << f6(ps, 1.1, 1.1) << endl;
function<double(Plus&&, double, double)> f7 = &Plus::plusd;
cout << f7(Plus(), 1.1, 1.1) << endl;
return 0;
}
2. std::bind
std::bind
是一个函数绑定器,它允许将一个函数和其参数绑定在一起,创建一个新的可调用对象。std::bind
可以将部分参数预设,从而生成一个“部分应用”的函数。
(1)特点
-
参数绑定:可以将函数的部分参数绑定为固定值,生成一个新的函数。
-
灵活性:可以绑定普通函数、成员函数、函数对象等。
-
占位符:使用占位符(如
std::placeholders::_1
)可以指定在调用时动态传入的参数位置。
(2)使用场景
-
预设参数:将部分参数固定,生成新的函数。
-
成员函数调用:绑定成员函数和对象,生成可调用的函数对象。
-
回调函数:生成预设参数的回调函数。
(3)代码测试
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;
int sub(int a,int b)
{
return (a - b) * 10;
}
int subx(int a, int b, int c)
{
return (a - b - c) * 10;
}
class Plus
{
public:
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return a + b;
}
};
int main()
{
auto sub1 = bind(sub, _1, _2);
cout << sub1(10, 5)<<endl; //10对应_1,5对应_2,_1传给a,_2传给b
// bind 本质返回的一个仿函数对象
// // 调整参数顺序(不常用)
// // _1代表第一个实参
// // _2代表第二个实参
// // ...
// // 调整参数熟顺序
auto sub2 = bind(sub, _2, _1);
cout << sub2(10, 5) << endl; //5对应_1,10对应_2,_1传给a,_2传给b
// 调整参数个数
// 绑死a
auto sub3 = bind(sub, 100, _1);
cout << sub3(5) << endl; //相当于a=100,b=_1=5
// 绑死b
auto sub4 = bind(sub, _1, 100);
cout << sub4(5) << endl;
// 分别绑死第123个参数
auto sub5 = bind(subx, 100, _1, _2);
cout << sub5(5, 1) << endl;
auto sub6 = bind(subx, _1, 100, _2);
cout << sub6(5, 1) << endl;
auto sub7 = bind(subx, _1, _2, 100);
cout << sub7(5, 1) << endl;
// 成员函数对象进行绑死,就不需要每次都传递了
function<double(Plus&&, double, double)> f6 = &Plus::plusd;
Plus pd;
cout << f6(move(pd), 1.1, 1.1) << endl;
cout << f6(Plus(), 1.1, 1.1) << endl;
// bind一般用于,绑死一些固定参数
function<double(double, double)> f7 = bind(&Plus::plusd, Plus(), _1, _2);
cout << f7(1.1, 1.1) << endl;
return 0;
}
3.应用
计算复利
在经济学中利率的计算可以经常用到bind,具体请见下述示例
using placeholders::_1;
int main()
{
auto func1 = [](double rate, double money, int year)->double {
double ret = money;
for (int i = 0; i < year; i++)
{
ret += ret * rate;
}
return ret - money;
};
cout << func1(0.1, 1000000, 30) << endl;
// 绑死一些参数,实现出支持不同年华利率,不同金额和不同年份计算出复利的结算利息
function<double(double)> func3_1_5 = bind(func1, 0.015, _1, 3);
function<double(double)> func5_2_5 = bind(func1, 0.025, _1, 5);
function<double(double)> func10_3_5 = bind(func1, 0.035, _1, 10);
function<double(double)> func20_5_5 = bind(func1, 0.055, _1, 20);
cout << func3_1_5(1000000) << endl;
cout << func5_2_5(1000000) << endl;
cout << func10_3_5(1000000) << endl;
cout << func20_5_5(1000000) << endl;
return 0;
}