std::function?,能存储任何符合模板参数的函数对象。换句话说,这些拥有一致参数类型、相同返回值类型(其实不必完全相同)的函数对象,可以由?std::function?统一包装起来。函数对象的大小是任意的、不能确定的,而C++中的类型都是固定大小的,那么,如何在一个固定大小的类型中存储任意大小的对象呢?
实际上问题还不止存储那么简单。存储了函数对象,必定是要在某一时刻调用;函数对象不是在创建的时候调用,这个特性成为延迟调用;函数对象也是对象,需要处理好构造、拷贝、移动、析构等问题——这些也需要延迟调用,但总不能再用?std::function?来解决吧?
既然?std::function?能存储不同类型的函数对象,可以说它具有多态性。C++中体现多态性的主要是虚函数,继承与多态这一套体制是可以解决这个问题的。相关资料[1]?[2]中的实现利用了继承与多态,相当简洁。
?
利用继承与多态,我们可以让编译器帮我们搞定函数对象的析构。就这种实现而言,这是简洁而有效的方法。然而这种实现需要动态内存,在一些情况下不划算,甚至完全没有必要。C++11引入了lambda表达式,其本质也是函数对象。这个对象有多大呢?取决于捕获列表。你写lambda会捕获多少东西?很多情况下就只是一对方括号而已吧。在这种情况下,lambda表达式的对象大小只有1字节(因为不能是0字节),你却为了这没有内容的1字节要调用动态内存的函数?C++注重运行时效率,这种浪费是不能接受的。
如何避免这种浪费呢?你也许会说我检查传入的对象是不是1字节的空类型。且不论这个trait怎么实现,函数指针、捕获一个int的lambda等类型都声称自己是trivial的小对象,也不应该分配到heap中去。
之前说过,std::function?的大小是固定的,但是这个大小是可以自己定的。我们可以在?std::function?的类定义中加入一个空白的、大小适中的field,用在存放这些小对象,从而避免这些情况下的动态内存操作。同时,既然有了这片空间,也就不需要看传入的对象是不是1字节的空类型了。
而对于更大的类型,虽然这个field不足以存放函数对象,但足以存放一个指针,这种分时复用的结构可以用union来实现。这种小对象直接存储、大对象在heap上开辟空间并存储指针的方法,称为small object optimization。
在利用继承的实现中,函数对象被包装在一个子类中,std::function?中持有一个其父类的指针。然而为了效率,我们需要把空白field和这个指针union起来。union总给人一种底层的感觉,在不确定这个union到底存储的是什么的时候,当然不能通过其中的指针去调用虚函数。在这样的设计中,多态性不再能用继承体系实现了,我们需要另一种实现多态的方法。
?
回想一下虚函数是如何实现的?带有virtual function的类的对象中会安插vptr,这个指针指向一个vtable,这个vtable含有多个slot,里面含有指向type_info对象的指针与函数指针——对,我们需要函数指针!不知你有没有在C中实现过多态,在没有语言特性的帮助下,比较方便的方法是在struct中直接放函数指针。如果要像C++那样用上vptr和vtable,你得管理好每个类及其对应vtable的内容。你以为这种情况在C++中就有所好转吗?只有你用C++的继承体系,编译器才会帮你做这些事。想要自己建立一个从类型到vptr的映射,恐怕你得改编译器了。(更正:C++14引入了变量模板,请移步C++值多态:传统多态与类型擦除之间。)
vptr与vtable的意义是什么?其一,每个基类只对应一个vptr,大小固定,多重继承下便于管理,但这点与这篇文章的主题没有关联;其二,当基类有多个虚函数的时候,使用vptr可以节省存储对象的空间,而如果用函数指针的话,虽然少了一次寻址,但继承带来的空间overhead取决于虚函数的数量,由于至少一个,函数指针的占用的空间不会少于vptr,在虚函数数量较多的情况下,函数指针就要占用比较大的空间了。
既然我们已经无法在?std::function?中使用vptr,我们也应该尽可能减少函数指针的数量,而这又取决于这些函数的功能,进一步取决于?std::function?类的接口。
?
虽然C++标准规定了?std::function?的接口就应该是这样,我还是想说说它为什么应该是这样。关于其他的一些问题,比如保存值还是保存引用等,可以参考相关资料[4]。
最基本的,std::function?是一个模板类,模板参数是一个类型(注意是一个类型,不是好几个类型)。我们可以这么写:
std::function<int(double)> f;
f?是一个可调用对象,参数为?double,返回值为?int?。你也许会问,这里既规定了参数类型又规定了返回值类型,怎么就成了一个类型呢?确实是一个类型,int(double)?是一个函数类型(注意不是函数指针)。
std::function?要包装所有合适类型的对象,就必须有对应的构造函数,所以这是个模板构造函数。参数不是通用引用而是直接传值:
template <typename F> function(F);
可能是为了让编译器对空对象进行优化。同样还有一个模板赋值函数,参数是通用引用。
每个构造函数都有一个添加了?std::allocator_arg_t?作为第一个参数、内存分配器对象作为第二个参数的版本,C++17中已经移除(GCC从未提供,可能是因为?std::function?的内存分配无法自定义)。同样删除的还有?assign?,也是与内存分配器相关的。
另外有一个以?std::reference_wrapper?作为参数的赋值函数:
template <typename F> function& operator=(std::reference_wrapper<F>) noexcept;
可以理解为模板赋值函数的特化。没有相应的构造函数。
默认构造函数、nullptr_t?构造函数、nullptr_t?拷贝赋值函数都将?std::function?对象置空。当?std::function?对象没有保存任何函数对象时,?operator?bool()?返回?false?,与?nullptr_t?调用?operator==?会返回?true?,如果调用将抛出?std::bad_function_call?异常。
虽然?std::function?将函数对象包装了起来,但用户还是可以获得原始对象的。target_type()?返回函数对象的?typeid?,target()?模板函数当模板参数与函数对象类型相同时返回其指针,否则返回空指针。
作为函数包装器,std::function?也是函数对象,可以通过?operator()?调用,参数按照模板参数中声明的类型传递。
还有一些接口与大部分STL设施相似,有Rule of Five规定的5个方法、?swap()?,以及?std::swap()?的特化等。可别小看这个?swap()?,它有大用处。
总之,函数对象的复制、移动、赋值、交换等操作都是需要的。对客户来说,除了两个?std::function?的相等性判定(笔者最近在尝试实现这个)以外,其他能想到的方法它都有。
?
std::function?的实现位于?<functional>?,后续版本迁移至了?<bits/std_function.h>?。下面这段代码是GCC 4.8.1(第一个支持完整C++11的版本)中的?<functional>?头文件,共2579行,默认折叠,慎入。
?functional
这个实现的原理与上面分析的大致相同,使用函数指针实现多态,也使用了small object optimization。
注:标准库的文件的缩进是2格,有时8个空格会用一个tab代替,在将tab显示为4字节的编辑器中缩进会比较乱,我已经把tab全部替换为8个空格;很多人缩进习惯是4格,但如果把2格全部替换成4格也会乱了格式,所以以下摘录自标准库文件的代码全部都是2格缩进。
?
类型之间的关系,无非是继承、嵌套、组合。这个实现中三者都有。
关于继承,你也许会问,我们刚才不是说了这种实现没法用继承吗?实际上没有矛盾。刚才说的继承,是接口上的继承,讲得更具体点就是要继承虚函数,是一种is-a的关系;而这里的继承,是实现上的继承,是一种is-implemented-in-terms-of的关系,在语言层面大多是private继承。
在泛型编程中,还有一个关于继承的问题,就是在继承体系的哪一层引入模板参数。
嵌套,即类中定义嵌套类型,使类之间的结构更加清晰,在泛型编程中还可以简化设计。
组合,在于一个类的对象中包含其他类的对象,本应属于对象关系的范畴,但是在这个实现中,一个类一般不会在同一个scope内出现多个对象,因此我这里就直接把对象组合的概念拿来用了。
2.1.1 异常类
首先出现的是?bad_function_call?类型,这是一个异常类,当调用空?std::function?对象时抛出:
1 class bad_function_call : public std::exception 2 { 3 public: 4 virtual ~bad_function_call() noexcept; 5 const char* what() const noexcept; 6 };
由于不是模板类(难得能在STL中发现非模板类),实现被编译好放在了目标文件中。虽然GCC开源,但既然这个类不太重要,而且稍微想想就能知道它是怎么实现的了,所以这里就不深究了。
相关的还有一个用于抛出异常的函数:
1 void __throw_bad_function_call() __attribute__((__noreturn__));
在?<bits/functexcept.h>?中。同样只有声明没有定义。
2.1.2 数据存储
有关数据存储的类共有3个:
1 class _Undefined_class; 2 3 union _Nocopy_types 4 { 5 void* _M_object; 6 const void* _M_const_object; 7 void (*_M_function_pointer)(); 8 void (_Undefined_class::*_M_member_pointer)(); 9 }; 10 11 union _Any_data 12 { 13 void* _M_access() { return &_M_pod_data[0]; } 14 const void* _M_access() const { return &_M_pod_data[0]; } 15 16 template<typename _Tp> 17 _Tp& 18 _M_access() 19 { return *static_cast<_Tp*>(_M_access()); } 20 21 template<typename _Tp> 22 const _Tp& 23 _M_access() const 24 { return *static_cast<const _Tp*>(_M_access()); } 25 26 _Nocopy_types _M_unused; 27 char _M_pod_data[sizeof(_Nocopy_types)]; 28 };
_Undefined_class?,顾名思义,连定义都没有,只是用于声明?_Nocopy_types?中的成员指针数据域,因为同一个平台上成员指针的大小是相同的。
_Nocopy_types?,是4种类型的联合体类型,分别为指针、常量指针、函数指针与成员指针。“nocopy”指的是这几种类型指向的对象类型,而不是本身。
_Any_data?,是两种类型的联合体类型,一个是?_Nocopy_types?,另一个是?char?数组,两者大小相等。后者是POD的,POD的好处多啊,memcpy可以用,最重要的是复制不会抛异常。非模板?_M_access()?返回指针,模板?_M_access()?返回解引用的结果,两者都有?const?重载。
2.1.3 辅助类
1 enum _Manager_operation 2 { 3 __get_type_info, 4 __get_functor_ptr, 5 __clone_functor, 6 __destroy_functor 7 };
_Manager_operation?,枚举类,是前面所说控制?std::function?的函数指针需要的参数类型。定义了4种操作:获得?type_info?、获得仿函数(就是函数对象)指针、复制仿函数、销毁(析构)仿函数。从这个定义中可以看出,1.4节所说的各种功能中,需要延迟调用的,除了函数对象调用以外,都可以通过这4个功能来组合起来。我们后面还会进一步探讨这个问题。
1 template<typename _Tp> 2 struct __is_location_invariant 3 : integral_constant<bool, (is_pointer<_Tp>::value 4 || is_member_pointer<_Tp>::value)> 5 { };
__is_location_invariant?,一个trait类,判断一个类型是不是“位置不变”的。从字面上来理解,一个类型如果是“位置不变”的,那么对于一个这种类型的对象,无论它复制到哪里,各个对象的底层表示都是相同的。在这个定义中,一个类型是“位置不变”的,当且仅当它是一个指针或成员指针,与一般的理解有所不同(更新:后来改为?template<typename _Tp>?struct?__is_location_invariant : is_trivially_copyable<_Tp>::type { };?,这就比较容易理解了)。
1 template<typename _Tp> 2 struct _Simple_type_wrapper 3 { 4 _Simple_type_wrapper(_Tp __value) : __value(__value) { } 5 6 _Tp __value; 7 }; 8 9 template<typename _Tp> 10 struct __is_location_invariant<_Simple_type_wrapper<_Tp> > 11 : __is_location_invariant<_Tp> 12 { };
_Simple_type_wrapper?,一个简单的包装器,用于避免?void*?与指针的指针之间类型转换的?const?问题。以及?__is_location_invariant?对?_Simple_type_wrapper?的偏特化。
2.1.4 内存管理基类
类?_Function_base?定义了一系列用于管理函数对象内存的函数,这是一个非模板类:
1 class _Function_base 2 { 3 public: 4 static const std::size_t _M_max_size = sizeof(_Nocopy_types); 5 static const std::size_t _M_max_align = __alignof__(_Nocopy_types); 6 7 template<typename _Functor> 8 class _Base_manager; 9 10 template<typename _Functor> 11 class _Ref_manager; 12 13 _Function_base() : _M_manager(0) { } 14 15 ~_Function_base() 16 { 17 if (_M_manager) 18 _M_manager(_M_functor, _M_functor, __destroy_functor); 19 } 20 21 bool _M_empty() const { return !_M_manager; } 22 23 typedef bool (*_Manager_type)(_Any_data&, const _Any_data&, 24 _Manager_operation); 25 26 _Any_data _M_functor; 27 _Manager_type _M_manager; 28 };
_Function_base?是?std::function?的实现基类,定义了两个静态常量,用于后面的trait类;两个内部类,用于包装静态方法;函数指针类型?_Manager_type?的对象?_M_manager?,用于存取?_Any_data?类型的?_M_functor?中的数据;构造函数,将函数指针置为空;析构函数,调用函数指针,销毁函数对象;_M_empty()?方法,检测内部是否存有函数对象。
我们来看其中的?_Base_manager?类:
1 template<typename _Functor> 2 class _Base_manager 3 { 4 protected: 5 static const bool __stored_locally = 6 (__is_location_invariant<_Functor>::value 7 && sizeof(_Functor) <= _M_max_size 8 && __alignof__(_Functor) <= _M_max_align 9 && (_M_max_align % __alignof__(_Functor) == 0)); 10 11 typedef integral_constant<bool, __stored_locally> _Local_storage; 12 13 static _Functor* 14 _M_get_pointer(const _Any_data& __source); 15 16 static void 17 _M_clone(_Any_data& __dest, const _Any_data& __source, true_type); 18 19 static void 20 _M_clone(_Any_data& __dest, const _Any_data& __source, false_type); 21 22 static void 23 _M_destroy(_Any_data& __victim, true_type); 24 25 static void 26 _M_destroy(_Any_data& __victim, false_type); 27 28 public: 29 static bool 30 _M_manager(_Any_data& __dest, const _Any_data& __source, 31 _Manager_operation __op); 32 33 static void 34 _M_init_functor(_Any_data& __functor, _Functor&& __f); 35 36 template<typename _Signature> 37 static bool 38 _M_not_empty_function(const function<_Signature>& __f); 39 40 template<typename _Tp> 41 static bool 42 _M_not_empty_function(const _Tp*& __fp); 43 44 template<typename _Class, typename _Tp> 45 static bool 46 _M_not_empty_function(_Tp _Class::* const& __mp); 47 48 template<typename _Tp> 49 static bool 50 _M_not_empty_function(const _Tp&); 51 52 private: 53 static void 54 _M_init_functor(_Any_data& __functor, _Functor&& __f, true_type); 55 56 static void 57 _M_init_functor(_Any_data& __functor, _Functor&& __f, false_type); 58 };
定义了一个静态布尔常量?__stored_locally?,它为真当且仅当?__is_location_invariant?trait为真、仿函数放得下、仿函数的align符合两个要求。然后再反过来根据这个值定义trait类?_Local_storage?(标准库里一般都是根据value trait来生成value)。
其余几个静态方法,顾名思义即可。有个值得思考的问题,就是为什么?_M_init_functor?是public的,没有被放进?_M_manager?呢?
再来看?_Ref_manager?类:
1 template<typename _Functor> 2 class _Ref_manager : public _Base_manager<_Functor*> 3 { 4 typedef _Function_base::_Base_manager<_Functor*> _Base; 5 6 public: 7 static bool 8 _M_manager(_Any_data& __dest, const _Any_data& __source, 9 _Manager_operation __op); 10 11 static void 12 _M_init_functor(_Any_data& __functor, reference_wrapper<_Functor> __f); 13 };
_Ref_manager?继承自?_Base_manager?类,覆写了两个静态方法。
2.1.5 仿函数调用
起辅助作用的模板函数?__callable_functor?:
1 template<typename _Functor> 2 inline _Functor& 3 __callable_functor(_Functor& __f) 4 { return __f; } 5 6 template<typename _Member, typename _Class> 7 inline _Mem_fn<_Member _Class::*> 8 __callable_functor(_Member _Class::* &__p) 9 { return std::mem_fn(__p); } 10 11 template<typename _Member, typename _Class> 12 inline _Mem_fn<_Member _Class::*> 13 __callable_functor(_Member _Class::* const &__p) 14 { return std::mem_fn(__p); } 15 16 template<typename _Member, typename _Class> 17 inline _Mem_fn<_Member _Class::*> 18 __callable_functor(_Member _Class::* volatile &__p) 19 { return std::mem_fn(__p); } 20 21 template<typename _Member, typename _Class> 22 inline _Mem_fn<_Member _Class::*> 23 __callable_functor(_Member _Class::* const volatile &__p) 24 { return std::mem_fn(__p); }
对非成员指针类型,直接返回参数本身;对成员指针类型,返回?mem_fn()?的结果(将类对象转换为第一个参数;这个标准库函数的实现不在这篇文章中涉及),并有cv-qualified重载。它改变了调用的形式,把所有的参数都放在了小括号中。
_Function_handler?类,管理仿函数调用:
1 template<typename _Signature, typename _Functor> 2 class _Function_handler; 3 4 template<typename _Res, typename _Functor, typename... _ArgTypes> 5 class _Function_handler<_Res(_ArgTypes...), _Functor> 6 : public _Function_base::_Base_manager<_Functor> 7 { 8 typedef _Function_base::_Base_manager<_Functor> _Base; 9 10 public: 11 static _Res 12 _M_invoke(const _Any_data& __functor, _ArgTypes... __args); 13 }; 14 15 template<typename _Functor, typename... _ArgTypes> 16 class _Function_handler<void(_ArgTypes...), _Functor> 17 : public _Function_base::_Base_manager<_Functor> 18 { 19 typedef _Function_base::_Base_manager<_Functor> _Base; 20 21 public: 22 static void 23 _M_invoke(const _Any_data& __functor, _ArgTypes... __args); 24 }; 25 26 template<typename _Res, typename _Functor, typename... _ArgTypes> 27 class _Function_handler<_Res(_ArgTypes...), reference_wrapper<_Functor> > 28 : public _Function_base::_Ref_manager<_Functor> 29 { 30 typedef _Function_base::_Ref_manager<_Functor> _Base; 31 32 public: 33 static _Res 34 _M_invoke(const _Any_data& __functor, _ArgTypes... __args); 35 }; 36 37 template<typename _Functor, typename... _ArgTypes> 38 class _Function_handler<void(_ArgTypes...), reference_wrapper<_Functor> > 39 : public _Function_base::_Ref_manager<_Functor> 40 { 41 typedef _Function_base::_Ref_manager<_Functor> _Base; 42 43 public: 44 static void 45 _M_invoke(const _Any_data& __functor, _ArgTypes... __args); 46 }; 47 48 template<typename _Class, typename _Member, typename _Res, 49 typename... _ArgTypes> 50 class _Function_handler<_Res(_ArgTypes...), _Member _Class::*> 51 : public _Function_handler<void(_ArgTypes...), _Member _Class::*> 52 { 53 typedef _Function_handler<void(_ArgTypes...), _Member _Class::*> 54 _Base; 55 56 public: 57 static _Res 58 _M_invoke(const _Any_data& __functor, _ArgTypes... __args); 59 }; 60 61 template<typename _Class, typename _Member, typename... _ArgTypes> 62 class _Function_handler<void(_ArgTypes...), _Member _Class::*> 63 : public _Function_base::_Base_manager< 64 _Simple_type_wrapper< _Member _Class::* > > 65 { 66 typedef _Member _Class::* _Functor; 67 typedef _Simple_type_wrapper<_Functor> _Wrapper; 68 typedef _Function_base::_Base_manager<_Wrapper> _Base; 69 70 public: 71 static bool 72 _M_manager(_Any_data& __dest, const _Any_data& __source, 73 _Manager_operation __op); 74 75 static void 76 _M_invoke(const _Any_data& __functor, _ArgTypes... __args); 77 };
共有6个特化版本:返回值类型为?void?、其他;函数对象类型为?std::reference_wrapper?、成员指针、其他。
继承自?_Function_base::_Base_manager?或?_Function_base::_Ref_manager?,提供了静态方法?_M_invoke()?,用于仿函数调用。有一个覆写的?_M_manager()?,表面上看是一个偏特化有覆写,实际上是两个,因为返回非?void?的成员指针偏特化版本还继承了其对应?void?偏特化版本。
2.1.6 接口定义
终于回到伟大的?std::function?了,但是我们还得再看点别的:
1 template<typename _Arg, typename _Result> 2 struct unary_function 3 { 4 typedef _Arg argument_type; 5 6 typedef _Result result_type; 7 }; 8 9 template<typename _Arg1, typename _Arg2, typename _Result> 10 struct binary_function 11 { 12 typedef _Arg1 first_argument_type; 13 14 typedef _Arg2 second_argument_type; 15 16 typedef _Result result_type; 17 };
std::unary_function?与?std::binary_function?,定义了一元和二元函数的参数类型与返回值类型。
1 template<typename _Res, typename... _ArgTypes> 2 struct _Maybe_unary_or_binary_function { }; 3 4 template<typename _Res, typename _T1> 5 struct _Maybe_unary_or_binary_function<_Res, _T1> 6 : std::unary_function<_T1, _Res> { }; 7 8 template<typename _Res, typename _T1, typename _T2> 9 struct _Maybe_unary_or_binary_function<_Res, _T1, _T2> 10 : std::binary_function<_T1, _T2, _Res> { };
_Maybe_unary_or_binary_function?类,当模板参数表示的函数为一元或二元时,分别继承?std::unary_function?与?std::binary_function?。
现在可以给出?std::function?类定义与方法声明:
1 template<typename _Signature> 2 class function; 3 4 template<typename _Res, typename... _ArgTypes> 5 class function<_Res(_ArgTypes...)> 6 : public _Maybe_unary_or_binary_function<_Res, _ArgTypes...>, 7 private _Function_base 8 { 9 typedef _Res _Signature_type(_ArgTypes...); 10 11 template<typename _Functor> 12 using _Invoke = decltype(__callable_functor(std::declval<_Functor&>()) 13 (std::declval<_ArgTypes>()...) ); 14 15 template<typename _CallRes, typename _Res1> 16 struct _CheckResult 17 : is_convertible<_CallRes, _Res1> { }; 18 19 template<typename _CallRes> 20 struct _CheckResult<_CallRes, void> 21 : true_type { }; 22 23 template<typename _Functor> 24 using _Callable = _CheckResult<_Invoke<_Functor>, _Res>; 25 26 template<typename _Cond, typename _Tp> 27 using _Requires = typename enable_if<_Cond::value, _Tp>::type; 28 29 public: 30 typedef _Res result_type; 31 32 function() noexcept; 33 34 function(nullptr_t) noexcept; 35 36 function(const function& __x); 37 38 function(function&& __x); 39 40 // TODO: needs allocator_arg_t 41 42 template<typename _Functor, 43 typename = _Requires<_Callable<_Functor>, void>> 44 function(_Functor); 45 46 function& 47 operator=(const function& __x); 48 49 function& 50 operator=(function&& __x); 51 52 function& 53 operator=(nullptr_t); 54 55 template<typename _Functor> 56 _Requires<_Callable<_Functor>, function&> 57 operator=(_Functor&& __f); 58 59 template<typename _Functor> 60 function& 61 operator=(reference_wrapper<_Functor> __f) noexcept; 62 void swap(function& __x); 63 64 // TODO: needs allocator_arg_t 65 /* 66 template<typename _Functor, typename _Alloc> 67 void 68 assign(_Functor&& __f, const _Alloc& __a); 69 */ 70 71 explicit operator bool() const noexcept; 72 73 _Res operator()(_ArgTypes... __args) const; 74 75 #ifdef __GXX_RTTI 76 const type_info& target_type() const noexcept; 77 78 template<typename _Functor> _Functor* target() noexcept; 79 80 template<typename _Functor> const _Functor* target() const noexcept; 81 #endif 82 83 private: 84 typedef _Res (*_Invoker_type)(const _Any_data&, _ArgTypes...); 85 _Invoker_type _M_invoker; 86 }; 87 88 template<typename _Res, typename... _Args> 89 inline bool 90 operator==(const function<_Res(_Args...)>& __f, nullptr_t) noexcept; 91 92 template<typename _Res, typename... _Args> 93 inline bool 94 operator==(nullptr_t, const function<_Res(_Args...)>& __f) noexcept; 95 96 template<typename _Res, typename... _Args> 97 inline bool 98 operator!=(const function<_Res(_Args...)>& __f, nullptr_t) noexcept; 99 100 template<typename _Res, typename... _Args> 101 inline bool 102 operator!=(nullptr_t, const function<_Res(_Args...)>& __f) noexcept; 103 104 template<typename _Res, typename... _Args> 105 inline void 106 swap(function<_Res(_Args...)>& __x, function<_Res(_Args...)>& __y);
前面说过,std::function?类的模板参数是一个函数类型。一个函数类型也是一个类型;std::function?只在模板参数是函数类型时才有意义;因此,有用的?std::function?是一个特化的模板,需要一个声明。标准库规定没有特化的声明是没有定义的。
std::function?继承自两个类:公有继承模板类?_Maybe_unary_or_binary_function?,私有继承非模板类?_Function_base?。
前者是公有继承,但实际上没有继承虚函数,不属于接口继承,而是实现继承,继承的是基类定义的类型别名。因为这些类型别名是面向客户的,所以必须公有继承。这个继承使?std::function?在不同数量的模板参数的实例化下定义不同的类型别名。继承是实现这种功能的唯一方法,SFINAE不行。(这是本文第一次出现SFINAE这个词,我默认你看得懂。这是泛型编程中的常用技巧,如果不会请参考这篇文章或Google。)
后者是私有继承,也属于实现继承,继承了基类的两个数据域与几个静态方法。
_Signature_type?是一个类型别名,就是模板参数,是一个函数类型。
_Invoke?是一个别名模板,就是仿函数被按参数类型调用的返回类型。如果不能调用,根据SFINAE,S错误不会E,但这个别名只有一个定义,在使用的地方所有S都E了,编译器还是会给E。
_CheckResult?是一个trait类,检测第一个模板参数能否转换为第二个。另有第二个参数为?void?的偏特化,在类型检测上使返回类型为?void?的?std::function?对象能支持任何返回值的函数对象。
_Callable?也是一个trait类,利用上面两个定义检测仿函数类型与?std::function?模板参数是否匹配。
_Requires?是一个有趣的别名模板,如果模板参数中第一个value trait为?true?,则定义为第二个模板参数,否则未定义(是没有,不是?void?),使用时将交给SFINAE处理。它大致上实现了C++20中?require?关键字的功能。实际上concept在2005年就有proposal了,一路从C++0x拖到C++20。我计划在C++20标准正式发布之前写一篇文章完整介绍concept。
result_type?是模板参数函数类型的返回值类型,与基类中定义的相同。
在类定义最后的私有段,还定义了一个函数指针类型以及该类型的一个对象,这是第二个函数指针。
其余的各种函数,在1.4节都介绍过了。
2.1.7 类型关系
讲了这么多类型,你记住它们之间的关系了吗?我们再来自顶向下地梳理一遍。
一个?std::function?对象中包含一个函数指针,它会被初始化为?_Function_handler?类中的静态函数的指针。std::function?与?_Function_handler?类之间,可以认为是组合关系。
std::function?继承自?_Maybe_unary_or_binary_function?与?_Function_base?,两者都是实现继承。
_Function_base?中有?_Base_manager?与?_Ref_manager?两个嵌套类型,其中后者还继承了前者,并覆写了几个方法。两个类定义了一系列静态方法,继承只是为了避免代码重复。