文章目录
- 1. function包装器
- 1.1 function包装器介绍
- 1.2 function包装器对类型的统一
- 1.3 function包装器的意义
1. function包装器
1.1 function包装器介绍
function包装器是一种函数包装器,也叫做适配器。它可以对可调用对象进行包装,C++中的function本质就是一个类模板。
template <class T> function;
template <class Ret, class... Args>
class Function<Ret(args...)>;
- Ret :被包装的可调用对象的返回值类型。
- Args… :被包装的课调用对象的形参类型。
包装示例
function包装器可以对可调用对象进行包装,包括函数指针(函数名)、仿函数(函数对象)、lambda表达式、类的成员函数。
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()
{
// 1、包装函数指针(函数名)
function<int(int, int)> func1 = f;
cout << func1(1, 2) << endl;
// 2、包装仿函数(函数对象)
function<int(int, int)> func2 = Functor();
cout << func2(1, 2) << endl;
// 3、包装lambda表达式
function<int(int, int)> func3 = [](int a, int b) {return a + b; };
cout << func3(1, 2) << endl;
// 4、包装静态成员函数
function<int(int, int)> func4 = &Plus::plusi; // &可省略
cout << func4(1, 2) << endl;
// 5、包装类的非静态成员函数
function<double(Plus, double, double)> func5 = &Plus::plusd; // &不可省略
cout << func5(Plus(), 1.1, 2.2) << endl;
return 0;
}
- 包装时指明返回值类型和各形参类型,然后可调用对象赋值给function包装器即可,包装后function对象就可以像普通函数一样使用了。
- 取静态成员函数的地址可以不用取地址运算符 & ,但取非静态成员函数地址使用 & 。
- 包装费静态的成员函数需要注意,非静态成员函数的第一个参数是隐藏this指针,因此在包装时需要指明第一个形参的类型为类的类型。
1.2 function包装器对类型的统一
对于以下函数模板useF:
- 传入该函数模板的第一个参数可以是任意的,比如函数指针,仿函数,lambda表达式等等。
- useF中定义了静态变量count,并在每次调用时将count的值和地址进行了打印,可判断多次调用时调用的是否是同一个useF函数。
1.3 function包装器的意义
- 将可调用对象的类型进行统一,便于我们对其进行统一化管理。
- 包装后明确了可调用对象的返回值和形参类型,更加方便使用者使用。
在传入第二个参数类型相同的情况下,如果传入的可调用对象的类型是不同的,那么在编译阶段该函数模板就会被实例化多次。
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;
}
运行结果如下:
由于函数指针、仿函数、lambda表达式是不同的类型,因此useF函数会被实例化出三份,三次调用useF锁打印count的地址也是不同的。
- 但实际这里根本没有必要实例化出三份useF函数,因为三次调用时传入的可调用对象虽然是不同类型的,但这三个可调用对象的返回值和形参类型都是相同的。
- 这时就可以用包装器对这三个可调用对象进行包装,然后再用这三个包装后的可调用对象来调用useF函数,这时就只会实例化出一份useF函数。
- 根本原因就是因为包装后,这三个可调用对象都是相同的function类型,因此最终只会实例化出一份useF函数,该函数的第一个模板参数的类型就是function类型的。
运行结果如下:
这时三次调用useF函数所打印count的地址就是相同的,并且count在三次调用后会被累加到3,表示这一个useF函数被调用了三次。