【导读】:本文主要讲解 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
从上面我们可以发现,std::function可以表示函数,lamada,可调用类对象。
2. std::function实现
在标准库中STL设计为如下:
template
上面的std::function继承关系比较简单,主要使用
union_Storage { //storageforsmallobjects(basic_stringissmall) max_align_t_Dummy1;//formaximumalignment char_Dummy2[_Space_size];//topermitaliasing _Ptrt*_Ptrs[_Num_ptrs];//_Ptrs[_Num_ptrs-1]isreserved };
这个来存储我们设置的可调用对象,我们从std::function的使用过程看一下整个原理。
2.1 函数对象赋值
我们使用的时候一般使用f = Caller;来设置函数对象,我们看下这个的实现过程。
template
我们看this->_Reset(_Func)这个函数,因为这个才是设置函数可调用对象的东西。
void_Set(_Ptrt*_Ptr)_NOEXCEPT {//storepointertoobject _Mystorage._Ptrs[_Num_ptrs-1]=_Ptr; } void_Reset_impl(_Fx&&_Val,const_Alloc&_Ax, _Myimpl*,_Alimpl&_Al,false_type) {//storecopyof_Valwithallocator,small(locallystored) _Myimpl*_Ptr=static_cast<_Myimpl *>(_Getspace()); _Al.construct(_Ptr,_STDforward<_Fx>(_Val),_Ax); _Set(_Ptr); } template
这个代码的主要意思就是创建一个_Func_impl<_Decayed, _Alloc, _Ret, _Types...>指针,然后赋值_Mystorage._Ptrs[_Num_ptrs - 1] = _Ptr;。
设置之后,我们看下调用操作怎么完成。
2.2 operator() 的实现
调用操作主要是通过operator()来实现的,我们看下这个的实现过程。
_Ptrt*_Getimpl()const_NOEXCEPT {//getpointertoobject return(_Mystorage._Ptrs[_Num_ptrs-1]); } _Retoperator()(_Types..._Args)const {//callthroughstoredobject if(_Empty()) _Xbad_function_call(); return(_Getimpl()->_Do_call(_STDforward<_Types>(_Args)...)); }
因此,我们是通过_Func_impl<_Decayed, _Alloc, _Ret, _Types...>转发了调用操作_Do_call
2.3 _Func_impl的实现
class_Func_impl :public_Func_base<_Rx, _Types...> {//derivedclassforspecificimplementationtypes public: typedef_Func_impl<_Callable, _Alloc, _Rx, _Types...>_Myt; typedef_Func_base<_Rx, _Types...>_Mybase; typedef_Wrap_alloc<_Alloc>_Myalty0; typedeftypename_Myalty0::templaterebind<_Myt>::other_Myalty; typedefis_nothrow_move_constructible<_Callable>_Nothrow_move; virtual_Rx_Do_call(_Types&&..._Args) {//callwrappedfunction return(_Invoke_ret(_Forced<_Rx>(),_Callee(), _STDforward<_Types>(_Args)...)); } _Compressed_pair<_Alloc, _Callable>_Mypair; };
_Func_impl这个类通过_Do_call来转发函数对象的调用操作。
3. 总结
这里我们看下std::function的结构信息,如下:
从这里我们发现_Storage大小为:
constint_Num_ptrs=6+16/sizeof(void*); constsize_t_Space_size=(_Num_ptrs-1)*sizeof(void*);
_Num_ptrs值为10。
如果我们赋值的对象有成员变量会是什么情况呢?例如如下:
classCCaller { public: intoperator()(inta,intb,intc,intd) { 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
内存结构如下:
由此我们可以发现std::function是利用一个_Compressed_pair<_Alloc, _Callable> _Mypair;拷贝了元素的数据信息。
主要的初始化过程为:
emplate
其中decay<_Fx>::type定义了_Compressed_pair<_Alloc, _Callable> _Mypair;中_Callable的类型,这个声明如下(也就是去掉引用和其他属性信息):
template
至此,我们大致上完成了std::function的原理分析了,希望在后续的使用中,我们能够知道std::function在什么情况下可以使用,以及背后完成的事情。
责任编辑:lq
-
函数
+关注
关注
3文章
4276浏览量
62316 -
C++
+关注
关注
21文章
2097浏览量
73452
原文标题:C++ std::function 技术浅谈
文章出处:【微信号:LinuxHub,微信公众号:Linux爱好者】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论