包装器
在C++11标准中,没有提供内置的包装器功能,但我们可以使用一些技术手段来实现包装器的效果。下面介绍两种常用的方法:
函数对象包装器
- 函数对象包装器(Function Object Wrapper): C++11引入了
std::function
模板类,它可以用于封装各种函数对象(如函数指针、lambda函数、可调用对象等),使其具备统一的调用接口。通过std::function
,你可以创建能够存储和调用各种函数类型的对象。
首先看下面代码
template<class F, class T>
T useF(F f, T x)
{
// 打印出 count 的值和地址
static int count = 0;
cout << "count: " << ++count << endl;
cout << "&count -> " << &count << endl;
return f(x);
}
double f(double i)
{
return i / 3;
}
struct Functor
{
double operator()(double d)
{
return d / 2;
}
};
int main()
{
// 函数名 做参数
cout << useF(f, 11.4) << endl;
// 函数对象 做参数
cout << useF(Functor(), 11.4) << endl;
// lambda 表达式 做参数
cout << useF([](double d)->double { return d / 4; }, 11.4) << endl;
// 上述执行过程中,useF 模板实例化了三份
return 0;
}
对于上述代码有 执行结果:
可以看出,三次&count 的结果是不同的,即useF模板实例化了三次,原因分析:
- 对于上述代码,useF 模板函数在每次调用时都需要进行实例化,因为传递给 useF 函数的函数对象或函数指针具有不同的函数签名。
- f 函数的函数签名是 double(double),Functor 函数对象的函数签名是 double operator()(double),而 lambda 表达式的函数签名是 (double)->double。
而使用包装器可以较好的解决这个问题
函数类型包装器
定义
#include <functional>
template <class Ret, class... Args>
class function<Ret(Args...)> {
// 实现代码
};
- Ret: 被调用函数的返回类型
- Args…:被调用函数的形参
使用方法
不改变上述代码,将main函数的执行代码使用 function
包装器的形势:
以下为执行结果:
可以看出 &count 的结果相同,即 useF
只进行了一次模板实例化
- 对于给定的代码,useF 模板函数只需要实例化一次是因为在调用 useF 函数时,模板参数的类型已经确定。每个模板实例化都是在编译时完成的,而不是在运行时。
- 在主函数中,我们分别使用了 func1、func2 和 func3 来实例化 useF 模板函数。这三个 std::function 对象具有相同的函数签名 double(double),因此 useF 模板函数只需要实例化一次来处理这个特定的函数签名。
bind
定义
- std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。
- 一般情况下,它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺
序调整等操作
基本语法
#include <functional>
auto bound_function = std::bind(function, args...);
-
bound_function
本身是一个可调用对象,args...
是一个逗号分隔的参数列表,对应给定的
function
的参数。 -
当我们调用 bound_function 时,bound_function 会调用 function,并传给它args…中
的参数。
在对bind 的描述中,有这么一句话:
即: 每个参数可以绑定到一个值,也可以是一个占位符
-
arg...
中的参数可能包含形如_n
的名字,其中n是一个整数,这些参数是 “占位符” ,表示bound_function
的参数,它们占据了传递给bound_function
的参数的“位置”。 -
数值n表示生成的可调用对象中参数的位置: _1 代表
bound_function
的第一个参数 , _2代表第二个参数 ,以此类推
使用
int Plus(int a, int b)
{
// 两数相加
return a + b;
}
class Sub
{
public:
int sub(int a, int b)
{
// 两数相减
return a - b;
}
};
using placeholders::_1;
using placeholders::_2;
对上面代码,我们进行不同的测试:
测试1
测试2