讲解C++ function 技术的实现与具体运用

描述

【导读】:本文主要讲解 C++ function 技术的实现与具体运用。

std::function是一个函数对象的包装器,std::function的实例可以存储,复制和调用任何可调用的目标,包括:

函数。

lamada表达式。

绑定表达式或其他函数对象。

指向成员函数和指向数据成员的指针。

当std::function对象没有初始化任何实际的可调用元素,调用std::function对象将抛出std::bad_function_call异常。

本文我们来谈一下std::function的实现原理。

1. std::function简介

在讨论其原理的时候,我们来熟悉一下这个东西是怎么使用的,C++标准库详细说明了这个的基本使用http://www.cplusplus.com/reference/functional/function/.

这里我们大概总结一下。

1.1 Member types

 

result_type 返回类型
argument_type 如果函数对象只有一个参数,那么这个代表参数类型。
first_argument_type 如果函数对象有两个个参数,那么这个代表第一个参数类型。
second_argument_type 如果函数对象有两个个参数,那么这个代表第二个参数类型。
成员类型 说明

 

1.2 Member functions

 

constructor 构造函数:constructs a new std::function instance
destructor 析构函数:destroys a std::function instance
operator= 给定义的function对象赋值
operator bool 检查定义的function对象是否包含一个有效的对象
operator() 调用一个对象
成员函数声明 说明

 

1.3 基本使用

#include  #include  int fun(int a, int b, int c, int d) {  std::cout << a << std::endl;  std::cout << b << std::endl;  std::cout << c << std::endl;  std::cout << d << std::endl;  return 0; } class CCaller { public:  int operator()(int a, int b, int c, int d)  {   std::cout << a << std::endl;   std::cout << b << std::endl;   std::cout << c << std::endl;   std::cout << d << std::endl;   return 0;  } }; int main() {  CCaller Caller;  std::function f;  f = [](int a, int b, int c, int d) -> int  {   std::cout << a << std::endl;   std::cout << b << std::endl;   std::cout << c << std::endl;   std::cout << d << std::endl;   return 0;  };  f(1, 2, 3, 4);  f = Caller;  f(10, 20, 30, 40);  f = fun;  f(100, 200, 300, 400);     return 0; }

从上面我们可以发现,std::function可以表示函数,lamada,可调用类对象。

2. std::function实现

在标准库中STL设计为如下:

template  struct _Arg_types  { // provide argument_type, etc. (sometimes)  }; template  struct _Arg_types<_Ty1>  { // provide argument_type, etc. (sometimes)  typedef _Ty1 argument_type;  }; template  struct _Arg_types<_Ty1, _Ty2>  { // provide argument_type, etc. (sometimes)  typedef _Ty1 first_argument_type;  typedef _Ty2 second_argument_type;  }; template  class _Func_class   : public _Arg_types<_Types...>  { // implement function template public:  typedef _Ret result_type;  typedef _Func_class<_Ret, _Types...> _Myt;  typedef _Func_base<_Ret, _Types...> _Ptrt;      private:  bool _Local() const _NOEXCEPT   { // test for locally stored copy of object   return (_Getimpl() == _Getspace());   }  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;         }; template   struct _Get_function_impl<_Ret CALL_OPT (_Types...)>   { /* determine type from argument list */   typedef _Func_class<_Ret, _Types...> type;   }; template  class function   : public _Get_function_impl<_Fty>::type  { // wrapper for callable objects public:  typedef function<_Fty> _Myt;     };

上面的std::function继承关系比较简单,主要使用

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 };

这个来存储我们设置的可调用对象,我们从std::function的使用过程看一下整个原理。

2.1 函数对象赋值

我们使用的时候一般使用f = Caller;来设置函数对象,我们看下这个的实现过程。

template   _Myt& operator=(reference_wrapper<_Fx> _Func) _NOEXCEPT {     // assign wrapper holding reference_wrapper to function object     this->_Tidy();     this->_Reset(_Func);     return (*this); }

我们看this->_Reset(_Func)这个函数,因为这个才是设置函数可调用对象的东西。

void _Set(_Ptrt *_Ptr) _NOEXCEPT { // store pointer to object  _Mystorage._Ptrs[_Num_ptrs - 1] = _Ptr; } 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); } template  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   void _Reset(_Fx&& _Val) {     // store copy of _Val     _Reset_alloc(_STD forward<_Fx>(_Val), allocator()); }

这个代码的主要意思就是创建一个_Func_impl<_Decayed, _Alloc, _Ret, _Types...>指针,然后赋值_Mystorage._Ptrs[_Num_ptrs - 1] = _Ptr;。

设置之后,我们看下调用操作怎么完成。

2.2 operator() 的实现

调用操作主要是通过operator()来实现的,我们看下这个的实现过程。

_Ptrt *_Getimpl() const _NOEXCEPT { // get pointer to object  return (_Mystorage._Ptrs[_Num_ptrs - 1]); } _Ret operator()(_Types... _Args) const { // call through stored object  if (_Empty())   _Xbad_function_call();  return (_Getimpl()->_Do_call(_STD forward<_Types>(_Args)...)); }

因此,我们是通过_Func_impl<_Decayed, _Alloc, _Ret, _Types...>转发了调用操作_Do_call

2.3 _Func_impl的实现

class _Func_impl  : public _Func_base<_Rx, _Types...> { // derived class for specific implementation types public:  typedef _Func_impl<_Callable, _Alloc, _Rx, _Types...> _Myt;  typedef _Func_base<_Rx, _Types...> _Mybase;  typedef _Wrap_alloc<_Alloc> _Myalty0;  typedef typename _Myalty0::template rebind<_Myt>::other _Myalty;  typedef is_nothrow_move_constructible<_Callable> _Nothrow_move;  virtual _Rx _Do_call(_Types&&... _Args)  { // call wrapped function   return (_Invoke_ret(_Forced<_Rx>(), _Callee(),    _STD forward<_Types>(_Args)...));  }     _Compressed_pair<_Alloc, _Callable> _Mypair; };

_Func_impl这个类通过_Do_call来转发函数对象的调用操作。

3. 总结

这里我们看下std::function的结构信息,如下:

从这里我们发现_Storage大小为:

const int _Num_ptrs = 6 + 16 / sizeof (void *); const size_t _Space_size = (_Num_ptrs - 1) * sizeof (void *);

_Num_ptrs值为10。

如果我们赋值的对象有成员变量会是什么情况呢?例如如下:

class CCaller { public:  int operator()(int a, int b, int c, int d)  {   std::cout << a << std::endl;   std::cout << b << std::endl;   std::cout << c << std::endl;   std::cout << d << std::endl;   return 0;  }  int a = 1;  int b = 10;  int c = 100; }; int main() {  CCaller Caller;  std::function f;  f = Caller;  f(10, 20, 30, 40);     return 0; }

内存结构如下:

由此我们可以发现std::function是利用一个_Compressed_pair<_Alloc, _Callable> _Mypair;拷贝了元素的数据信息。

主要的初始化过程为:

emplate  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>()); }

其中decay<_Fx>::type定义了_Compressed_pair<_Alloc, _Callable> _Mypair;中_Callable的类型,这个声明如下(也就是去掉引用和其他属性信息):

template  struct decay  { // determines decayed version of _Ty  typedef typename remove_reference<_Ty>::type _Ty1;  typedef typename _If::value,   typename remove_extent<_Ty1>::type *,   typename _If::value,    typename add_pointer<_Ty1>::type,    typename remove_cv<_Ty1>::type>::type>::type type;  };

至此,我们大致上完成了std::function的原理分析了,希望在后续的使用中,我们能够知道std::function在什么情况下可以使用,以及背后完成的事情。

责任编辑:lq

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分