当前位置 博文首页 > 我爱编程:转载functional

    我爱编程:转载functional

    作者:[db:作者] 时间:2021-07-02 15:34

    一、std::function的原理与接口

    1.1?std::function是函数包装器

    std::function?,能存储任何符合模板参数的函数对象。换句话说,这些拥有一致参数类型、相同返回值类型(其实不必完全相同)的函数对象,可以由?std::function?统一包装起来。函数对象的大小是任意的、不能确定的,而C++中的类型都是固定大小的,那么,如何在一个固定大小的类型中存储任意大小的对象呢?

    实际上问题还不止存储那么简单。存储了函数对象,必定是要在某一时刻调用;函数对象不是在创建的时候调用,这个特性成为延迟调用;函数对象也是对象,需要处理好构造、拷贝、移动、析构等问题——这些也需要延迟调用,但总不能再用?std::function?来解决吧?

    既然?std::function?能存储不同类型的函数对象,可以说它具有多态性。C++中体现多态性的主要是虚函数,继承与多态这一套体制是可以解决这个问题的。相关资料[1]?[2]中的实现利用了继承与多态,相当简洁。

    ?

    1.2 C++注重运行时效率

    利用继承与多态,我们可以让编译器帮我们搞定函数对象的析构。就这种实现而言,这是简洁而有效的方法。然而这种实现需要动态内存,在一些情况下不划算,甚至完全没有必要。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到底存储的是什么的时候,当然不能通过其中的指针去调用虚函数。在这样的设计中,多态性不再能用继承体系实现了,我们需要另一种实现多态的方法。

    ?

    1.3 用函数指针实现多态

    回想一下虚函数是如何实现的?带有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?类的接口。

    ?

    1.4?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的实现

    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格缩进。

    ?

    2.1 类型系统

    类型之间的关系,无非是继承、嵌套、组合。这个实现中三者都有。

    关于继承,你也许会问,我们刚才不是说了这种实现没法用继承吗?实际上没有矛盾。刚才说的继承,是接口上的继承,讲得更具体点就是要继承虚函数,是一种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?两个嵌套类型,其中后者还继承了前者,并覆写了几个方法。两个类定义了一系列静态方法,继承只是为了避免代码重复。

    下一篇:没有了