function类的实现中涉及到的类:
template<class _Rx,
class... _Types>
class _Func_base
{...}
保存了其关联函数的类型信息,返回值类型Rx,各入参类型列表_Types,这些模板形参在模板类的实现中能够获取到。
template<class _Callable,
class _Alloc,
class _Rx,
class... _Types>
class _Func_impl final
: public _Func_base<_Rx, _Types...>
在模板形参中多了两个参数:
_Callable可调用对对象类型,可是函数指针,也可以是实现operator()操作符的可调用实体。
_Alloc内存分配器,负责_Func_impl对象创建时做内存分配。allocator是STL中非常重要的部分,如容器中元素所用内存空间的分配都是由allocator负责,其对内存获取的方式做了抽象。
A. 可调用对象的保存
在_Func_imp中有如下成员
_Compressed_pair<_Alloc, _Callable> _Mypair;
该成员是一个pair类型,保存内存分配器及可调用类型的对象。_Compressed_pair为类模板,可适配保存任意类型的可调用对象。_Func_impl仅有如下pubic的构造函数,在构造函数中初始化其Pair成员,何时调用该构造函数在后面说明。
template<class _Other1,
class _Other2>
_Func_impl(_Other1&& _Val, _Other2&& _Ax)
: _Mypair(_One_then_variadic_args_t(),
_STD forward<_Other2>(_Ax), _STD forward<_Other1>(_Val))
{ // construct
}
B. _Func_imp的复制、转移和调用
复制逻辑如下:
typedef _Func_impl<_Callable, _Alloc, _Rx, _Types...> _Myt;
...
// 小内存类型
template<class _Void>
_Mybase *_Clone(_Void *_Where, false_type) const
{ // return clone of *this, small (locally stored)
_Myalty _Al(_Myax());
_Myt * _Ptr = static_cast<_Myt *>(_Where);
_Al.construct(_Ptr, _Callee(), _Myax());
return (_Ptr);
}
// 大内存类型
template<class _Void>
_Mybase *_Clone(_Void *, true_type) const
{ // return clone of *this, large (dynamically allocated)
_Myalty _Al(_Myax());
_Myt * _Ptr = _Al.allocate(1);
_TRY_BEGIN
_Al.construct(_Ptr, _Callee(), _Myax());
_CATCH_ALL
_Al.deallocate(_Ptr, 1);
_RERAISE;
_CATCH_END
return (_Ptr);
}
在指定内存地址位置_Ptr处,分配并初始化_Func_impl对象。实现上对,内存分配和管理做了优化,通过最后一个入参区分大小内存类型:大内存类型由内存分配器动态分配内存,小内存对象则用入参指定的内存。好处在于,通过入参指定的内存地址是预先分配固定内存,以此避免_Func_impl在创建时频繁地申请内存。所以本质上还是新实例的拷贝构造,只不过在内存分配上借助了allocator,隐藏了内存分配细节。这些接口的定义都是为了配合function对象在转移或复制时,作为其内部的成员的_Func_imp完成自身复制。
调用逻辑如下:
_Callable& _Callee() _NOEXCEPT
{ // return reference to wrapped function
return (_Mypair._Get_second()); // !!!返回Pair中保存的Callable对象
}
// 入参为右值引用,函数调用时使用foward模板做实参转发
virtual _Rx _Do_call(_Types&&... _Args)
{ // call wrapped function
return (_Invoke_ret(_Forced<_Rx>(), _Callee(),
_STD forward<_Types>(_Args)...));
}
其实就是透传参数,间接调用Callable对象。可以推测在function对象调用时会透传给该接口。同时注意的是,其入参是与模板变参列表一致的。
// TEMPLATE CLASS _Func_class
template<class _Ret,
class... _Types>
class _Func_class
: public _Arg_types<_Types...>
{
...
union _Storage
{ // storage for small objects (basic_string is small)
max_align_t _Dummy1; // for maximum alignment
char _Dummy2[_Space_size]; // to permit aliasing
_Ptrt *_Ptrs[_Num_ptrs]; // _Ptrs[_Num_ptrs - 1] is reserved
};
_Storage _Mystorage;
}
_Func_class是最外层的类,_Storage用于保存了_Func_impl对象。内存上使用union,与_Func_impl的复制转移的优化策略相匹配:小内存对象时用&_MyStorage作为预分配内存,否则由分配器动态分配,而且将创建返回的对象地址记录在_Ptrs[_Num_ptrs-1]中。而且注意到_Func_class的模板形参中并没有_Callable和_Alloc,说明该两个形参是在_Func_impl的创建(或拷贝)处通过函数模板形参传入的。关键代码如下:
protected:
template<class _Fx>
using _Result_of_invoking_t = result_of_t<_Fx(_Types...)>;
template<class _Inv_res>
using _Enable_if_returnable_t = enable_if_t<
is_convertible<_Inv_res, _Ret>::value || is_void<_Ret>::value>;
template<class _Fx>
void _Reset(_Fx&& _Val)
{ // store copy of _Val
_Reset_alloc(_STD forward<_Fx>(_Val), allocator<int>());
}
template<class _Fx,
class _Alloc>
void _Reset_alloc(_Fx&& _Val, const _Alloc& _Ax)
{ // store copy of _Val with allocator
if (!_Test_callable(_Val))
{ // null member pointer/function pointer/std::function
return; // already empty
}
typedef typename decay<_Fx>::type _Decayed;
typedef _Func_impl<_Decayed, _Alloc, _Ret, _Types...> _Myimpl;
_Myimpl *_Ptr = 0;
typedef _Wrap_alloc<_Alloc> _Alimpl0;
typedef typename _Alimpl0::template rebind<_Myimpl>::other _Alimpl;
_Alimpl _Al(_Ax);
_Reset_impl(_STD forward<_Fx>(_Val), _Ax,
_Ptr, _Al, _Is_large<_Myimpl>());
}
template<class _Fx,
class _Alloc,
class _Myimpl,
class _Alimpl>
void _Reset_impl(_Fx&& _Val, const _Alloc& _Ax,
_Myimpl *, _Alimpl& _Al, true_type)
{ // store copy of _Val with allocator, large (dynamically allocated)
_Myimpl *_Ptr = _Al.allocate(1);
_TRY_BEGIN
_Al.construct(_Ptr, _STD forward<_Fx>(_Val), _Ax);
_CATCH_ALL
_Al.deallocate(_Ptr, 1);
_RERAISE;
_CATCH_END
_Set(_Ptr);
}
template<class _Fx,
class _Alloc,
class _Myimpl,
class _Alimpl>
void _Reset_impl(_Fx&& _Val, const _Alloc& _Ax,
_Myimpl *, _Alimpl& _Al, false_type)
{ // store copy of _Val with allocator, small (locally stored)
_Myimpl *_Ptr = static_cast<_Myimpl *>(_Getspace());
_Al.construct(_Ptr, _STD forward<_Fx>(_Val), _Ax);
_Set(_Ptr);
}
bool _Empty() const _NOEXCEPT
{ // return true if no stored object
return (_Getimpl() == 0);
}
_Ptrt *_Getimpl() const _NOEXCEPT
{ // get pointer to object
return (_Mystorage._Ptrs[_Num_ptrs - 1]);
}
如上接口均为_Func_class中定义的protected接口,后面4)中可以看到function的继承自_Func_class,通过调用这些接口,实现impl对象的创建、释放及状态判断。在impl的创建上,如上几个接口的调用关系为_Reset—— >Reset_alloc——>_Reset_impl,_Cabllable在_Reset调用时传入,分配器默认是allocator。Reset_impl函数模板,接受Callable和Alloc形参类型,并作为_Func_impl模板的实参。整个创建过程与_Func_impl的自身复制逻辑类似,都是先分配内存,然后调用_Func_impl的构造函数做初始化,调用关系如下图。_Func_class是否为空本质上就是判断_Reset_impl对象是否为空。
4. function
至此,终于看到了我们熟悉的function,其定义如下:
// TEMPLATE CLASS _Get_function_impl
template<class _Tx>
struct _Get_function_impl;
#define _GET_FUNCTION_IMPL(CALL_OPT, X1, X2) \
template \
struct _Get_function_impl<_Ret CALL_OPT (_Types...)> \
{ /* determine type from argument list */ \
typedef _Func_class<_Ret, _Types...> type; \
};
_NON_MEMBER_CALL(_GET_FUNCTION_IMPL, , )
#undef _GET_FUNCTION_IMPL
// TEMPLATE CLASS function
template<class _Fty>
class function
: public _Get_function_impl<_Fty>::type
{ // wrapper for callable objects
private:
typedef typename _Get_function_impl<_Fty>::type _Mybase;
通常fuction的使用不会直接指定返回值和形参列表,而是function
template<class _Fx,
// 对调用对象的返回做校验,验证是否与function中指定的Ret一致
class _Inv_res = typename _Mybase::template _Result_of_invoking_t<_Fx&>,
class = typename _Mybase::template _Enable_if_returnable_t<_Inv_res> >
function(_Fx _Func)
{ // construct wrapper holding copy of _Func
this->_Reset(_STD move(_Func));
}
function(const _Myt& _Right)
{ // construct holding copy of _Right
this->_Reset_copy(_Right);
}
从构造函数可以看出,实现就是调用基类的_Reset接口,以完成_Func_impl对象的创建和初始化。在拷贝构造中,function的拷贝复制过程就是做_Func_impl对象的拷贝和复制。实现上有一个小的细节,调用函数的签名(返回值和入参)是在类模板的形参列表中指定的,而不是在构造函数function(_Fx _Func)入参类型_Fx中推导出来的,也就是说function对象可以接受任何与template class function中_Fy签名一致或兼容的调用对象。