一、 s t d : : f u n c t i o n std::function std::function的介绍
s t d : : f u n c t i o n std::function std::function是 C + + 11 C++11 C++11引入的一个可调用对象包装器,它可以通过指定模板参数,统一来处理各自可调用对象。
二、实现类似 s t d : : f u n c t i o n std::function std::function的功能
这里,我们一步步演示如何自定义一个类似 s t d : : f u n t i o n std::funtion std::funtion的类模板。
首先,我们声明一个泛化版本和特化版本的类:
//泛化版本
template<typename T>
class CallFuncObj; //用不到,声明即可
//特化版本
template<typename T,typename... Args>
class CallFuncObj<T(Args...)> {
};
因为泛化版本用不到,所以我们简单声明即可,重点是在特化版本上。
为了让 C a l l F u n c O b j CallFuncObj CallFuncObj能够接受任意的可调用对象作为实参,那么我们有必要为它实现一个构造函数模板,如下:
//构造函数模板,用于接受各种可调用对象
template<typename U>
CallFuncObj(U&& acobj);
这里使用万能引用,无论接受的是左值还是右值,都没有问题。
包装了可调用对象,我们还需要一个类能够调用它,因此我们引入一个新的类模板 C F O b j H a n d l e r CFObjHandler CFObjHandler,作为 C a l l F u n c O b j CallFuncObj CallFuncObj类的成员类型,它是一个指针,指向这个类:
template<typename T,typename... Args>
class CallFuncObj<T(Args...)> {
private:
CFObjHandler<T, Args...>* handler; //可调用对象处理器指针,父类指针
};
紧接着,我们要实现这个类。
在标准库中
s
t
d
:
:
f
u
n
c
t
i
o
n
std::function
std::function是通过虚函数来实现对可调用对象的调用的,因此我们也增加一个虚函数,在子类中实现它。
//添加可调用对象处理器
template<typename T,typename ...Args>
class CFObjHandler {
public:
//纯虚函数,后续要创建子类要实现它
virtual T invoke(Args... args) const = 0 ;
};
我们新增一个子类$CFObjHandlerChild $,然后继承
C
F
O
b
j
H
a
n
d
l
e
r
CFObjHandler
CFObjHandler类。
实现一下它的构造函数和
i
n
v
o
k
e
invoke
invoke虚函数:
//实现CFObjHandler的子类,用于接收在allFuncObj中构造函数的U&&类型参数
template<typename U,typename T,typename... Args>
class CFObjHandlerChild :public CFObjHandler<T, Args...> {
private:
U functor; //U是一个可调用对象,这里保存可调用对象的一个副本
public:
CFObjHandlerChild(U &&funcobj):functor(std::forward<U>(funcobj)) {} //完美转发到初始化列表,保持信息完整性
//重写一下虚函数
virtual T invoke(Args... args) const {
return functor(std::forward<Args>(args)...); //完美转发
}
};
注意这里构造函数用的也是万能引用,然后通过完美转发来实现构造。
最后,我们完善一下 C a l l F u n c O b j CallFuncObj CallFuncObj类,补充一下构造函数:
CallFuncObj(U&& acobj) { //注意这里是右值引用,才能是接收所有的参数类型
//初始化对象处理器指针,父类指针指向指针
handler = new CFObjHandlerChild<U, T, Args...>(std::forward<U>(acobj)); //完美转发
}
注意这里是用父类指针指向子类对象,由于用到万能引用,这里我们也是使用完美转发。
我们还需要重载以下 ( ) () ()运算符,让 C a l l F u n c O b j CallFuncObj CallFuncObj具备类似函数的功能。
具体而言就是直接使用 h a n d l e r handler handler来调用子类 C F O b j H a n d l e r C h i l d CFObjHandlerChild CFObjHandlerChild的 i n v o k e invoke invoke函数。
//重载operator()
T operator()(Args...args) const {
//调用可调用对象
return handler->invoke(std::forward<Args>(args)...); //使用完美转发,不丢失信息
}
然后别忘了补充一下析构函数,回收一下指针内存:
//析构函数
~CallFuncObj() {
delete handler;
}
完整代码如下:
//创建一个简单的函数包装器
//前向声明
template<typename T,typename... Args>
class CFObjHandler;
template<typename U, typename T, typename... Args>
class CFObjHandlerChild;
//泛化版本
template<typename T>
class CallFuncObj; //用不到,声明即可
//特化版本
template<typename T,typename... Args>
class CallFuncObj<T(Args...)> {
private:
CFObjHandler<T, Args...>* handler; //可调用对象处理器指针,父类指针
public:
//构造函数模板,用于接受各种可调用对象
template<typename U>
CallFuncObj(U&& acobj) { //注意这里是右值引用,才能是接收所有的参数类型
//初始化对象处理器指针,父类指针指向指针
handler = new CFObjHandlerChild<U, T, Args...>(std::forward<U>(acobj)); //完美转发
}
//重载operator()
T operator()(Args...args) const {
//调用可调用对象
return handler->invoke(std::forward<Args>(args)...); //使用完美转发,不丢失信息
}
//析构函数
~CallFuncObj() {
delete handler;
}
};
//添加可调用对象处理器
template<typename T,typename ...Args>
class CFObjHandler {
public:
//虚函数,后续要创建子类
virtual T invoke(Args... args) const {
return T{};
};
};
//实现CFObjHandler的子类,用于接收在allFuncObj中构造函数的U&&类型参数
template<typename U,typename T,typename... Args>
class CFObjHandlerChild :public CFObjHandler<T, Args...> {
private:
U functor; //U是一个可调用对象,这里保存可调用对象的一个副本
public:
CFObjHandlerChild(U &&funcobj):functor(std::forward<U>(funcobj)) {} //完美转发到初始化列表,保持信息完整性
//重写一下虚函数
virtual T invoke(Args... args) const {
return functor(std::forward<Args>(args)...); //完美转发
}
};
三、自定义函数包装器的测试
简单测试一下,这里包装三类可调用对象: l a m b a d a lambada lambada表达式、全局函数、仿函数:
//全局函数
void myfunc1(int tmprv) {
std::cout << "调用了myfunc1!\n";
std::cout << "tmprv = " << tmprv << "\n";
}
//仿函数
struct myfunc2 {
void operator()(int x, double y) const { //需要添加const修饰符,因为包装器内的虚函数invoke是const类型的
std::cout << "调用了myfunc2!\n";
}
};
//包装匿名函数
auto add = [](int x, int y) {
std::cout << "调用了add函数!\n";
return x + y;
};
测试函数:
void Test() {
//包装全局函数
CallFuncObj<void(int)>f1 = myfunc1;
f1(100);
CallFuncObj<int(int, int)>f2 = add;
int res = f2(1, 2);
std::cout << res << "\n";
//包装仿函数
CallFuncObj<void(int, double)> f3 = myfunc2{};
f3(1, 2.0);
}
测试结果如下,都能够正确运行: