温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

C++11中std::function的用法

发布时间:2021-06-18 13:38:02 来源:亿速云 阅读:326 作者:chen 栏目:开发技术

本篇内容主要讲解“C++11中std::function的用法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C++11中std::function的用法”吧!

目录
  • 1、源码准备

  • 2、std::function简介

  • 3、源码解析

    • 3.1、std::function解析

    • 3.2、std::_Function_handler解析

    • 3.3、_Any_data解析

    • 3.4、std::_Function_base解析

  • 4、总结

    1、源码准备

    本文是基于gcc-4.9.0的源代码进行分析,std::function是C++11才加入标准的,所以低版本的gcc源码是没有std::function的,建议选择4.9.0或更新的版本去学习,不同版本的gcc源码差异应该不小,但是原理和设计思想的一样的。
    源码下载地址:http://ftp.gnu.org/gnu/gcc

    2、std::function简介

    类模版std::function是一种通用的多态函数包装器。std::function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作,这些目标实体包括普通函数指针、类成员函数指针(第一个参数需要传入对应的this指针)、Lambda表达式或者某个类的实例(前提是这个类重载了()运算符)。std::function对象是对C++中现有的可调用实体的一种类型安全的包裹(我们知道像函数指针这类可调用实体,是类型不安全的)。

    通常std::function是一个函数对象类,它包装其它任意的可调用实体,被包装的对象具有类型为T1,…,TN的N个参数,并且返回一个可转换到R类型的值。std::function使用模板转换构造函数接收被包装的函数对象;特别是,闭包类型可以隐式地转换为std::function。最简单的理解就是通过std::function对C++中各种可调用实体的封装,形成一个新的可调用的std::function对象,让我们不再纠结那么多的可调用实体之间如何进行方便高效的转换。

    3、源码解析

    3.1、std::function解析

    std::function位于libstdc++-v3\include\std\functional中

    template<typename _Res, typename... _ArgTypes>
    class function<_Res(_ArgTypes...)> : public _Maybe_unary_or_binary_function<_Res, _ArgTypes...>, private _Function_base
    {
        typedef _Res _Signature_type(_ArgTypes...);
    
        template<typename _Functor>
        using _Invoke = decltype(__callable_functor(std::declval<_Functor&>())(std::declval<_ArgTypes>()...) );
    
        template<typename _Functor>
        using _Callable = __check_func_return_type<_Invoke<_Functor>, _Res>;
    
        template<typename _Cond, typename _Tp>
        using _Requires = typename enable_if<_Cond::value, _Tp>::type;
    
    public:
        typedef _Res result_type;
    
        function() noexcept
            :_Function_base()
        {
        }
    
        function(nullptr_t) noexcept
            :_Function_base()
        {
        }
    
        template<typename _Res, typename... _ArgTypes>
        function(const function& __x)
            :_Function_base()
        {
            if (static_cast<bool>(__x))
            {
                _M_invoker = __x._M_invoker;
                _M_manager = __x._M_manager;
                __x._M_manager(_M_functor, __x._M_functor, __clone_functor);
            }
        }
    
        function(function&& __x)
            :_Function_base()
        { __x.swap(*this); }
    
        template<typename _Functor, typename = _Requires<_Callable<_Functor>, void>>
        function(_Functor __f)
        {
            typedef _Function_handler<_Signature_type, _Functor> _My_handler;
    
            if (_My_handler::_M_not_empty_function(__f))
            {
                _My_handler::_M_init_functor(_M_functor, std::move(__f));
                _M_invoker = &_My_handler::_M_invoke;
                _M_manager = &_My_handler::_M_manager;
            }
        }
    
        function& operator=(const function& __x)
        {
            function(__x).swap(*this);
            return *this;
        }
    
        function& operator=(function&& __x)
        {
            function(std::move(__x)).swap(*this);
            return *this;
        }
    
        function& operator=(nullptr_t)
        {
            if (_M_manager)
            {
                _M_manager(_M_functor, _M_functor, __destroy_functor);
                _M_manager = 0;
                _M_invoker = 0;
            }
            return *this;
        }
    
        template<typename _Functor>
        _Requires<_Callable<_Functor>, function&> operator=(_Functor&& __f)
        {
            function(std::forward<_Functor>(__f)).swap(*this);
            return *this;
        }
    
        template<typename _Functor>
        function& operator=(reference_wrapper<_Functor> __f) noexcept
        {
            function(__f).swap(*this);
            return *this;
        }
    
        void swap(function& __x)
        {
            std::swap(_M_functor, __x._M_functor);
            std::swap(_M_manager, __x._M_manager);
            std::swap(_M_invoker, __x._M_invoker);
        }
    
         explicit operator bool() const noexcept
         { return !_M_empty(); }
    
        _Res operator()(_ArgTypes... __args) const;
        {
            if (_M_empty())
                __throw_bad_function_call();
            return _M_invoker(_M_functor, std::forward<_ArgTypes>(__args)...);
        }
    
    private:
        typedef _Res (*_Invoker_type)(const _Any_data&, _ArgTypes...);
        _Invoker_type _M_invoker;

    从源代码中可以看出以下几点信息:

    • 该类是一个可变参模板类

    • 该类继承于_Maybe_unary_or_binary_function(不分析)和_Function_base,类成员只有_M_invoker一个,从定义可以看出这是一个标准的函数指针

    • 首先分析operator()方法,平时开发中std::function使用最多的肯定就是重载的括号运算符了,毕竟最终也是要把它当成类似于函数的形式来调用的。可以看到operator()函数里面调用了_M_invoker函数,并没有什么特殊的处理

    • 既然_M_invoker能被调用,那就说明它肯定被初始化过了,从调用时传给他的参数来看,多了一个不知道是什么的参数_M_functor,所以我们可以猜测_M_invoker并不是直接指向std::function接管的可调用实体的,而是一个类似中间层的东西,在_M_invoker的实现里面才调用了我们需要执行的那个真实的可调用实体

    • 只有构造函数function(_Functor __f)对_M_invoker进行了初始化,而使用的就是std::_Function_handler里的方法来初始化_M_invoker的,std::_Function_handler的实现在后面会讲到

    • 还是看构造函数function(_Functor __f),因为std::function的目的就是对我们传入的可调用实体进行包装,这里说的可调用实体可以是普通函数指针、类成员函数指针(第一个参数需要传入对应的this指针)、Lambda表达式以及某个类实例(前提是这个类重载了()运算符),而我们看到在std::function这个类里面并没有直接托管我们传入的可调用实体,而只是调用了_My_handler::_M_init_functor(_M_functor, std::move(__f)),推测是由_Function_base来托管可调用实体的

    3.2、std::_Function_handler解析

    std::_Function_handler位于libstdc++-v3\include\std\functional中

    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)...);
        }
    };
    
    template<typename _Functor, typename... _ArgTypes>
    class _Function_handler<void(_ArgTypes...), _Functor> : public _Function_base::_Base_manager<_Functor>
    {
        typedef _Function_base::_Base_manager<_Functor> _Base;
    
    public:
        static void _M_invoke(const _Any_data& __functor, _ArgTypes... __args)
        {
            (*_Base::_M_get_pointer(__functor))(std::forward<_ArgTypes>(__args)...);
        }
    };
    
    template<typename _Res, typename _Functor, typename... _ArgTypes>
    class _Function_handler<_Res(_ArgTypes...), reference_wrapper<_Functor> > : public _Function_base::_Ref_manager<_Functor>
    {
        typedef _Function_base::_Ref_manager<_Functor> _Base;
    
    public:
        static _Res _M_invoke(const _Any_data& __functor, _ArgTypes... __args)
        {
            return __callable_functor(**_Base::_M_get_pointer(__functor))(std::forward<_ArgTypes>(__args)...);
        }
    };
    
    template<typename _Functor, typename... _ArgTypes>
    class _Function_handler<void(_ArgTypes...), reference_wrapper<_Functor> > : public _Function_base::_Ref_manager<_Functor>
    {
        typedef _Function_base::_Ref_manager<_Functor> _Base;
    
    public:
        static void _M_invoke(const _Any_data& __functor, _ArgTypes... __args)
        {
            __callable_functor(**_Base::_M_get_pointer(__functor))(std::forward<_ArgTypes>(__args)...);
        }
    };
    
    template<typename _Class, typename _Member, typename _Res, typename... _ArgTypes>
    class _Function_handler<_Res(_ArgTypes...), _Member _Class::*> : public _Function_handler<void(_ArgTypes...), _Member _Class::*>
    {
        typedef _Function_handler<void(_ArgTypes...), _Member _Class::*> _Base;
    
    public:
        static _Res _M_invoke(const _Any_data& __functor, _ArgTypes... __args)
        {
            return std::mem_fn(_Base::_M_get_pointer(__functor)->__value)(std::forward<_ArgTypes>(__args)...);
        }
    };
    
    template<typename _Class, typename _Member, typename... _ArgTypes>
    class _Function_handler<void(_ArgTypes...), _Member _Class::*> : public _Function_base::_Base_manager<_Simple_type_wrapper< _Member _Class::* > >
    {
        typedef _Member _Class::* _Functor;
        typedef _Simple_type_wrapper<_Functor> _Wrapper;
        typedef _Function_base::_Base_manager<_Wrapper> _Base;
    
    public:
        static bool _M_manager(_Any_data& __dest, const _Any_data& __source, _Manager_operation __op)
        {
            switch (__op)
            {
                #ifdef __GXX_RTTI
                case __get_type_info:
                    __dest._M_access<const type_info*>() = &typeid(_Functor);
                    break;
                #endif
    
                case __get_functor_ptr:
                    __dest._M_access<_Functor*>() = &_Base::_M_get_pointer(__source)->__value;
                    break;
    
                default:
                    _Base::_M_manager(__dest, __source, __op);
            }
            return false;
        }
    
        static void _M_invoke(const _Any_data& __functor, _ArgTypes... __args)
        {
            std::mem_fn(_Base::_M_get_pointer(__functor)->__value)(std::forward<_ArgTypes>(__args)...);
        }
    };

    从源代码中可以看出_Function_handler有六种重载形式,以下对其进行分类说明:

    • 第一个和第二个重载形式继承于std::_Function_base::_Base_manager,当std::function接管的可调用实体是一个普通函数指针、类实例或者Lambda表达式时,发挥作用的就是这两个类。里面的内容很简单,通过调用_Function_base::_Base_manager<_Functor>的_M_get_pointer方法从__functor中取出对应的可调用实体,然后直接执行,我们知道能直接执行的可调用实体的类型是普通函数指针、类实例(必须重载了()运算符)或者Lambda表达式(Lambda其实本质就是一个匿名的类实例)。这两个重载形式的唯一区别就是它们一个处理函数有返回值的情况,另一个处理没返回值的情况。

    • 第三个和第四个重载形式继承于std::_Function_base::_Ref_manager,可以看出它们基本上算是前两个类的偏特化版本,当第二个模板参数为std::reference_wrapper包装的引用时,就调用这两个偏特化的版本。这两个重载形式的唯一区别也是它们一个处理函数有返回值的情况,另一个处理没返回值的情况。现在问题来了,为什么要搞一个对于std::reference_wrapper类型的偏特化版本呢?这是因为如果可调用实体已经先被std::reference_wrapper包装过的话,那我们是绝对绝对不能直接调用这个可调用实体的,因为此时根本不确定这个被包装的可调用实体究竟是什么类型的,如果是类成员函数的话那当然是不能直接调用的,此时必须使用std::mem_fn来获取一个可调用的对象,类中使用的std::__callable_functor函数的实现如下面的代码所示,可以看到有好几种特化版本,当std::reference_wrapper包装的可调用实体是一个类成员函数指针时,就会通过std::mem_fn来获取一个可调用的对象,这和前面描述的内容一致。

    template<typename _Functor>
    inline _Functor& __callable_functor(_Functor& __f)
    { return __f; }
    
    template<typename _Member, typename _Class>
    inline _Mem_fn<_Member _Class::*> __callable_functor(_Member _Class::* &__p)
    { return std::mem_fn(__p); }
    
    template<typename _Member, typename _Class>
    inline _Mem_fn<_Member _Class::*> __callable_functor(_Member _Class::* const &__p)
    { return std::mem_fn(__p); }
    
    template<typename _Member, typename _Class>
    inline _Mem_fn<_Member _Class::*> __callable_functor(_Member _Class::* volatile &__p)
    { return std::mem_fn(__p); }
    
    template<typename _Member, typename _Class>
    inline _Mem_fn<_Member _Class::*> __callable_functor(_Member _Class::* const volatile &__p)
    { return std::mem_fn(__p); }

    关于上面提到的std::reference_wrapper和std::mem_fn,大家如果可以不懂的话一定要看下面两篇文章,不然的话就像学英语不会英语单词一样,根本不可能看懂std::function的内容的

    《C++11的std::ref、std::cref源码解析》
    《C++11的std::mem_fn源码解析》

    • 第五个和第六个重载形式和前面的差不多,这两个也是属于偏特化版本,主要是用于处理可调用实体为类成员函数指针的情况,这里就可以看到直接调用了std::men_fn函数来使得我们可以直接调用对应的类成员函数,从这点也可以看出std::men_fn函数的重要性,不懂的小伙伴一定要去看前面两篇文章啊。

    • 每一个类里面的_M_invoke函数都用了_M_get_pointer(来源不全部相同),从代码逻辑上不难看出_M_get_pointer函数的作用是从第一个传入参数__functor中取出对应的可调用实体,然后将后面的可变参传给这个可调用实体,运行它,这个功能是不是就有点似曾相识了?对,这就是我们平时正常调用函数的那样子嘛,也就是说std::function的函数执行功能在这里实现了

    • 从代码中我们可以看出这几个类都是和std::_Function_base相关的,并且到现在还是不知道_M_functor究竟是个什么东西,接下来分析std::_Function_base,看一下里面究竟做了哪些工作

    3.3、_Any_data解析

    _Any_data位于libstdc++-v3\include\std\functional中

    union _Nocopy_types
    {
        void*       _M_object;
        const void* _M_const_object;
        void (*_M_function_pointer)();
        void (_Undefined_class::*_M_member_pointer)();
    };
    
    union _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)];
    };

    看std::_Function_base之前先看一个重要的联合体_Any_data,这个在前面出现很多次了,但是一直没有介绍一下它究竟是个什么东西,下面简单分析一下:

    • 有两个联合体成员,一个是_M_unused(没卵用),一个是_M_pod_data,这两个的占用内存是一样的,具体原因就不讲了,大家可以自己用sizeof去试一下

    • 里面有四个_M_access函数,前两个是直接将_M_pod_data的地址返回出去,不做任何转换,后两个则是可以将_M_pod_data转换为任意类型返回,从这里可以看出这个_Any_data的作用就是来接管可调用对象的,所以后续可以通过各种转换将它还原为可调用的形式(比如前面提到的那个_Function_base::_Base_manager<_Functor>::_M_get_pointer方法就是干这个货活的)

    • 简单看一下_Nocopy_types这个联合体,前两个成员的寓意就是类实例或Lambda表达式,第三个寓意是普通函数指针,第四个寓意是类成员函数指针,仔细一看这不就是我们前面提到无数次的可调用实体的几种形式吗?这个_Nocopy_types从上下文看来并没有什么乱用,估计就是源码作者写给读者看的吧,让大家更容易读懂源码。

    3.4、std::_Function_base解析

    std::_Function_base的实现位于libstdc++-v3\include\std\functional中

    class _Function_base
    {
    public:
        static const std::size_t _M_max_size = sizeof(_Nocopy_types);
        static const std::size_t _M_max_align = __alignof__(_Nocopy_types);
    
        template<typename _Functor>
        class _Base_manager
        {
        protected:
            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;
    
            static _Functor* _M_get_pointer(const _Any_data& __source)
            {
                const _Functor* __ptr = __stored_locally? std::__addressof(__source._M_access<_Functor>()) : __source._M_access<_Functor*>();
                return const_cast<_Functor*>(__ptr);
            }
    
            static void _M_clone(_Any_data& __dest, const _Any_data& __source, true_type)
            {
                new (__dest._M_access()) _Functor(__source._M_access<_Functor>());
            }
    
            static void _M_clone(_Any_data& __dest, const _Any_data& __source, false_type)
            {
                __dest._M_access<_Functor*>() = new _Functor(*__source._M_access<_Functor*>());
            }
    
            static void _M_destroy(_Any_data& __victim, true_type)
            {
                __victim._M_access<_Functor>().~_Functor();
            }
    
            static void _M_destroy(_Any_data& __victim, false_type)
            {
                delete __victim._M_access<_Functor*>();
            }
    
        public:
            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;
            }
    
            static void _M_init_functor(_Any_data& __functor, _Functor&& __f)
            { _M_init_functor(__functor, std::move(__f), _Local_storage()); }
    
            template<typename _Signature>
            static bool _M_not_empty_function(const function<_Signature>& __f)
            { return static_cast<bool>(__f); }
    
            template<typename _Tp>
            static bool _M_not_empty_function(_Tp* const& __fp)
            { return __fp; }
    
            template<typename _Class, typename _Tp>
            static bool _M_not_empty_function(_Tp _Class::* const& __mp)
            { return __mp; }
    
            template<typename _Tp>
            static bool _M_not_empty_function(const _Tp&)
            { return true; }
    
        private:
            static void _M_init_functor(_Any_data& __functor, _Functor&& __f, true_type)
            { new (__functor._M_access()) _Functor(std::move(__f)); }
    
            static void _M_init_functor(_Any_data& __functor, _Functor&& __f, false_type)
            { __functor._M_access<_Functor*>() = new _Functor(std::move(__f)); }
        };
    
        template<typename _Functor>
        class _Ref_manager : public _Base_manager<_Functor*>
        {
            typedef _Function_base::_Base_manager<_Functor*> _Base;
    
        public:
            static bool _M_manager(_Any_data& __dest, const _Any_data& __source, _Manager_operation __op)
            {
                switch (__op)
                {
                    case __get_functor_ptr:
                        __dest._M_access<_Functor*>() = *_Base::_M_get_pointer(__source);
                        return is_const<_Functor>::value;
                        break;
    
                    default:
                        _Base::_M_manager(__dest, __source, __op);
                }
                return false;
            }
    
            static void _M_init_functor(_Any_data& __functor, reference_wrapper<_Functor> __f)
            {
                _Base::_M_init_functor(__functor, std::__addressof(__f.get()));
            }
        };
    
        _Function_base() : _M_manager(0) { }
    
        ~_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;
        _Manager_type _M_manager;
    };

    从源代码中可以看出以下几点信息:

    • 该类有两个类成员,分别是_M_functor和_M_manager,_M_functor就不用多说了,前面一直有提到它,他的类型_Any_data也在上一小节讲过了。而_M_manager则是一个函数指针,下面再介绍它有什么用。

    • 这里看一下_Function_base::_Base_manager类里面的各个方法

      • _M_init_functor函数:该函数前面提到过了,当时看到的是将std::function接管的可调用对象传递给了这个函数,而从前面我们知道_Any_data类型的数据是可以接管所有形式的可调用实体的,所以综合可得_M_init_functor函数的作用就是将传递给它的第二个参数储存到第一个参数中(_Any_data类型数据),这样就达成了接管可调用实体的功能了

      • _M_get_pointer函数:这个函数同样在前面提到过,当时我们只知道通过调用_M_get_pointer可以获取到我们接管的那个可调用实体,但是不知道是如何实现的,这里可以看出他的实现是非常简单的,也就是从它的传入参数(_Any_data类型数据)将可调用实体取出,并强制转换为合法的类型,里面用到了std::__addressof,作用就是即使目标变量(类)存在operator&的重载,也依然能够获得变量的地址。

      • 关于std::__addressof的详细内容可以看这篇文章《C++11的std::addressof源码解析》

      • _M_manager函数:该函数就是根据传入的第三个参数来确定执行不同的功能,其余的几个函数无非就是涉及到内存的分配和释放之类的,对我们理解std::function的影响不大,这里就不展开讲了

    • 接下来看一下_Function_base::_Ref_manager类

      • 可以看到该类继承于_Function_base::_Base_manager类,可见他拥有_Function_base::_Base_manager类实现的所有功能

      • 该类是处理当std::function接管的是一个引用包装类的情况的,为什么这种情况需要单独处理呢?因为此时如果直接对传入参数取地址,取到的将是引用包装类的地址,而不是我们要接管的可调用对象的地址,所以只能搞这么一个特化版本,就像_Function_base::_Ref_manager类做的那样,里面的_M_init_functor函数通过使用reference_wrapper的get方法获取到了std::function接管的可调用对象的真实地址

    • _Function_base类里的其它方法就不讲了,大家自己看一下吧,其余的实现基本都是基于前面讲的那些内容,比较简单

    4、总结

    本文先是简单介绍了std::function的用途(对C++中各种可调用实体进行封装),然后通过对源码进行详细分析,我们知道了std::function是如何实现对可调用实体进行封装的,源码内容会比较复杂,但是其中的设计思路是很值得我们学习借鉴的,尤其是与std::reference_wrapper和std::mem_fn配合的那部分代码更是精妙绝伦。

    到此,相信大家对“C++11中std::function的用法”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI