functional源码分析

    技术2024-06-20  79

    function类的实现中涉及到的类:

    _Func_base 最顶层的基础模板类,定义了函数对象在实例复制、转移上的纯虚接口,如Copy、Move等。同时定义了函数调用的虚接口_Do_call。这些纯虚接口要求在子类中实现。_Func_base的声明: template<class _Rx, class... _Types> class _Func_base {...}

    保存了其关联函数的类型信息,返回值类型Rx,各入参类型列表_Types,这些模板形参在模板类的实现中能够获取到。

    _Func_impl 模板子类,实现了_Func_base定义的所有虚接口。其声明有所不同: 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对象调用时会透传给该接口。同时注意的是,其入参是与模板变参列表一致的。

    _Func_class // 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<class _Ret, \ class... _Types> \ 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<Ret(arg1, arg2,…)>的方式,而_Func_class的模板形参中又是Ret和_Types…,如何转化?这其实就是_Get_function_impl的作用,通过模板偏特化,借助编译器的推导能力,从中分离出Ret和_Types…,构建_Func_class,通俗讲,“返回值”就是_Func_class<_Ret, _Types…>,所以function是继承自_Func_class的。紧接着看一个funciton的构造函数:

    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签名一致或兼容的调用对象。

    Processed: 0.010, SQL: 9