一、简介
std::function的实例可以存储、复制和调用任何可复制构造的可调用目标,包括普通函数、成员函数、类对象(重载了operator()的类的对象)、Lambda表达式等。是对C++现有的可调用实体的一种类型安全的包裹(相比而言,函数指针这种可调用实体,是类型不安全的)。
对于std::function的所有实例,其大小是一样的,但是可以包装不同大小的可调用对象。
二、源码分析
1、成员变量
//std_function.h line 88
template<typename _Res, typename... _ArgTypes>
class function<_Res(_ArgTypes...)>
: public _Maybe_unary_or_binary_function<_Res, _ArgTypes...>,
private _Function_base
{
.....
private:
using _Invoker_type = _Res (*)(const _Any_data&, _ArgTypes&&...);
_Invoker_type _M_invoker;
};
粗略地看,该类继承于_Maybe_unary_or_binary_function 和_Function_base,类成员变量只有 _M_invoker,从 _M_invoker 的定义可以看出这是一个标准的函数指针。
2、operator()
//std_function.h line 699
template<typename _Res, typename... _ArgTypes>
_Res function<_Res(_ArgTypes...)>::
operator()(_ArgTypes... __args) const
{
if (_M_empty())
__throw_bad_function_call();
return _M_invoker(_M_functor, std::forward<_ArgTypes>(__args)...);
}
class function重载了operator()操作符,operator()对函数指针_M_invoker进行调用,_M_invoker的第一个入参为_M_functor(_M_functor为基类_Function_base中的成员变量),类型为_Any_data。可以推测:_M_invoker并不直接管理可调用目标,而是对可调用目标进行调用;而可调用目标是被_Function_base::_M_functor管理的。
为了验证一下上述推测,接下来看一下class function实例在声明时是怎么初始化_M_invoker的。
3、构造函数
构造函数function(_Functor __f)完成了对_M_invoker的初始化。
//std_function.h line 683
template<typename _Res, typename... _ArgTypes>
template<typename _Functor, typename, typename>
function<_Res(_ArgTypes...)>::function(_Functor __f)
: _Function_base()
{
typedef _Function_handler<_Res(_ArgTypes...), _Functor> _My_handler;
if (_My_handler::_M_not_empty_function(__f))
{
// 通过函数名可以看出_Function_base::_M_functor管理了可调用目标。
_My_handler::_M_init_functor(_M_functor, std::move(__f));
// class function的_M_invoker被初始化为_My_handler::_M_invoke
_M_invoker = &_My_handler::_M_invoke;
// _My_handler::_M_invoke中完成对_Function_base::_M_functor管理目标的调用
_M_manager = &_My_handler::_M_manager;
}
}
4、_Function_Handler
查看模板类_Function_handler的源码,一共有四个特化版本。
template<typename _Res, typename _Functor, typename... _ArgTypes>
class _Function_handler<_Res(_ArgTypes...), _Functor>
: public _Function_base::_Base_manager<_Functor>
{
typedef _Function_base::_Base_manager<_Functor> _Base;
public:
static _Res
_M_invoke(const _Any_data& __functor, _ArgTypes&&... __args)
{
return (*_Base::_M_get_pointer(__functor))(
std::forward<_ArgTypes>(__args)...);
}
};
四个特化版本均继承自_Function_base::_Base_manager。其中第一个和第二个特化版本存储的可调用目标是普通函数、类实例或者Lambda表达式,两个特化版本的区别为第一个存储的可调用目标有返回值,第二个存储的可调用目标没有返回值;其中第三个和第四个特化版本存储的可调用目标是类的成员函数,区别在于成员函数是否有返回值。每一个特化版本均包含一个静态函数_M_invoke,使用该函数初始化function::_M_invoker,在该函数中完成可调用目标的调用。 可以看到_M_invoke()函数内调用了_Function_base::_Base_manager的方法。而且在3中,我们知道可调用目标是通过_M_init_functor函数存储的,_M_init_functor是_Function_base::_Base_manager的成员函数,因此我们接着分析_Function_base的源代码
5、class _Function_base
//std_function.h line 150
class _Function_base
{
public:
static const std::size_t _M_max_size = sizeof(_Nocopy_types); // 实际为2个指针的大小
static const std::size_t _M_max_align = __alignof__(_Nocopy_types);
_Function_base() : _M_manager(nullptr) { }
~_Function_base()
{
if (_M_manager)
_M_manager(_M_functor, _M_functor, __destroy_functor);
}
bool _M_empty() const { return !_M_manager; }
typedef bool (*_Manager_type)(_Any_data&, const _Any_data&,
_Manager_operation);
_Any_data _M_functor; // _M_functor接管可调用目标
_Manager_type _M_manager;
// 含有一个内部模板类_Base_manager
template<typename _Functor>
class _Base_manager
{
...... // 源码分析见第6节
};
};
类型_Any_data如下所示,为联合体。含有两个联合体成员:_M_unused和_M_pod_data,接管的可调用对象存储在中_M_pod_data,并通过四个_M_access()函数将_M_pod_data转换(或者说是还原)为可调用对象返回出去。
//std_function.h line 104
union [[gnu::may_alias]] _Any_data
{
void* _M_access() { return &_M_pod_data[0]; }
const void* _M_access() const { return &_M_pod_data[0]; }
template<typename _Tp>
_Tp& _M_access()
{ return *static_cast<_Tp*>(_M_access()); }
template<typename _Tp>
const _Tp& _M_access() const
{ return *static_cast<const _Tp*>(_M_access()); }
_Nocopy_types _M_unused;
char _M_pod_data[sizeof(_Nocopy_types)];
};
_Nocopy_types的定义如下,
//std_function.h line 94
class _Undefined_class;
union _Nocopy_types
{
void* _M_object; // 接管类实例或者Lambda表达式
const void* _M_const_object; // 接管类实例或者Lambda表达式
void (*_M_function_pointer)(); // 接管普通函数指针
void (_Undefined_class::*_M_member_pointer)(); // 接管类成员函数指针,其指针大小是16
};
目前为止,我们已经知道了可调用对象存在哪,但是还不知道如何存起来的,因为我们还没看_Base_manager::_M_init_functor的源码。接下来看一下类_Base_manager的源码。
6、class _Base_manager
对_Function_base的操作均是通过其内部模板类_Base_manager进行的,_Base_manager提供了很多接口,源码如下所示
//std_function.h line 157
template<typename _Functor>
class _Base_manager // 所有成员均为静态的
{
protected:
// __stored_locally 用来判断可调用目标存储在哪里
// 决定_M_pod_data是作为存储空间存储接管对象还是作为指针,指向存储了接管对象所在的地址空间
// 表达式的值主要取决于sizeof(_Functor),_M_max_size为 16
static const bool __stored_locally =
(__is_location_invariant<_Functor>::value
&& sizeof(_Functor) <= _M_max_size
&& __alignof__(_Functor) <= _M_max_align
&& (_M_max_align % __alignof__(_Functor) == 0));
typedef integral_constant<bool, __stored_locally> _Local_storage;
// 从_Function_base::_M_functor中取出可调用目标实体
//当重载了运算符 & 之后,要取地址就需要std::addressof 来获取地址了。
static _Functor* _M_get_pointer(const _Any_data& __source)
{
const _Functor* __ptr =
__stored_locally? std::__addressof(__source._M_access<_Functor>())
/* have stored a pointer */ : __source._M_access<_Functor*>();
return const_cast<_Functor*>(__ptr);
}
/*****************_Function_base::_M_functor的初始化********************/
// 形参__functor接收实参_Function_base::_M_functor
// 将形参__f存储在实参_Function_base::_M_functor中
static void _M_init_functor(_Any_data& __functor, _Functor&& __f)
{ _M_init_functor(__functor, std::move(__f), _Local_storage()); }
// 根据_Local_storage()调用以下两个private函数,_Local_storage()返回__stored_locally的取值
static void _M_init_functor(_Any_data& __functor, _Functor&& __f, true_type)
{ ::new (__functor._M_access()) _Functor(std::move(__f)); } // 使用placement new,存储在栈上
static void _M_init_functor(_Any_data& __functor, _Functor&& __f, false_type)
{ __functor._M_access<_Functor*>() = new _Functor(std::move(__f)); } //new, 存储在堆上
// 静态成员函数为_M_manager,实际上就是对内存的管理
// 根据第三个参数来确定执行的功能:获取、克隆、析构
static bool _M_manager(_Any_data& __dest, const _Any_data& __source, _Manager_operation __op)
{
switch (__op)
{
case __get_functor_ptr:
__dest._M_access<_Functor*>() = _M_get_pointer(__source);
break;
case __clone_functor:
_M_clone(__dest, __source, _Local_storage());
break;
case __destroy_functor:
_M_destroy(__dest, _Local_storage());
break;
}
return false;
}
// Destroying a location-invariant object may still require
// destruction.
static void _M_destroy(_Any_data& __victim, true_type)
{
// 存在本地的时候使用了placement new,所以需要显示调用析构
__victim._M_access<_Functor>().~_Functor();
}
// Destroying an object located on the heap.
static void
_M_destroy(_Any_data& __victim, false_type)
{
// 存在堆上的使用的是new,所以成对使用delete
delete __victim._M_access<_Functor*>();
}
};
总结
1、function的函数指针成员变量_M_invoker被初始化为_Function_handler的成员函数_M_invoker;_M_invoker调用了接管的可调用对象;
2、function的基类_Function_base的成员_M_functor真正接管可调用对象,当可调用对象的大小大于两个指针时存储在堆上,_M_functor指向存储位置;
3、_Function_base的内部类_Base_manager提供接口函数。
7、几个问题
- sizeof(std::function) = ?
32 - std::function的对象如何判断相等?
c++11中std::function并没有实现操作符 == 操作符来判断两个std::function对象是否相等,可以使用 target()函数,c++11标准的官方定义就是返回函数指针,但是使用target()方法需要传入类型。但是可以将function对象与nullptr进行比较。
//line 762
template<typename _Res, typename... _Args>
inline bool
operator==(const function<_Res(_Args...)>& __f, nullptr_t) noexcept
{ return !static_cast<bool>(__f); }
/// @overload
template<typename _Res, typename... _Args>
inline bool
operator==(nullptr_t, const function<_Res(_Args...)>& __f) noexcept
{ return !static_cast<bool>(__f); }
GDB验证
根据上述分析,当可调用对象的大小大于16字节时,将在堆上分配内存,且_M_pof_data内储存的是该对象的指针;当可调用对象的大小小于16字节时,将直接储存在_M_pof_data的内存区域();
1、绑定Lambda对象
2、绑定成员函数(16字节)
3、绑定成员函数及对象指针(24字节)
4、绑定成员函数及对象(大小不定)
参考文档
- https://zhuanlan.zhihu.com/p/560964284
- https://wenku.baidu.com/view/d0f1ce11f211f18583d049649b6648d7c1c7083e.html
- https://blog.csdn.net/FairLikeSnow/article/details/122051985?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0-122051985-blog-112335892.pc_relevant_layerdownloadsortv1&spm=1001.2101.3001.4242.1&utm_relevant_index=3(可变参数模板)
- https://blog.csdn.net/qq_60750110/article/details/125861059 (模板特化)
- https://www.cnblogs.com/xzlq/p/9504851.html (placement new)
- https://zhuanlan.zhihu.com/p/415639983
- http://blog.bitfoc.us/p/525
- https://zh.cppreference.com/w/cpp/utility/functional/function