• std::bind 源码分析


     std::bind 源码分析

    背景

    std::bind 功能可谓十分强大,下边代码来自于https://en.cppreference.com/w/cpp/utility/functional/bind,大家可以看下其实现功能很多,我也是很好奇这些功能是怎么实现的,尤其是std::placeholders::_1 ,可以调整参数顺序,这也太神奇了,接下来我就从源码角度来分析下,是怎么做到的

    C++
    #include
    #include
    #include
    #include
     
    void f(int n1, int n2, int n3, const int& n4, int n5)
    {
        std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
    }
     
    int g(int n1)
    {
        return n1;
    }
     
    struct Foo {
        void print_sum(int n1, int n2)
        {
            std::cout << n1+n2 << '\n';
        }
        int data = 10;
    };
     
    int main()
    {
        using namespace std::placeholders;  // for _1, _2, _3...
     
        std::cout << "1) argument reordering and pass-by-reference: ";
        int n = 7;
        // (_1 and _2 are from std::placeholders, and represent future
        // arguments that will be passed to f1)
        auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n);
        n = 10;
        f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused
                        // makes a call to f(2, 42, 1, n, 7)
     
        std::cout << "2) achieving the same effect using a lambda: ";
        n = 7;
        auto lambda = [&ncref=n, n](auto a, auto b, auto /*unused*/) {
            f(b, 42, a, ncref, n);
        };
        n = 10;
        lambda(1, 2, 1001); // same as a call to f1(1, 2, 1001)
     
        std::cout << "3) nested bind subexpressions share the placeholders: ";
        auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);
        f2(10, 11, 12); // makes a call to f(12, g(12), 12, 4, 5);
     
        std::cout << "4) bind a RNG with a distribution: ";
        std::default_random_engine e;
        std::uniform_int_distribution<> d(0, 10);
        auto rnd = std::bind(d, e); // a copy of e is stored in rnd
        for(int n=0; n<10; ++n)
            std::cout << rnd() << ' ';
        std::cout << '\n';
     
        std::cout << "5) bind to a pointer to member function: ";
        Foo foo;
        auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);
        f3(5);
     
        std::cout << "6) bind to a mem_fn that is a pointer to member function: ";
        auto ptr_to_print_sum = std::mem_fn(&Foo::print_sum);
        auto f4 = std::bind(ptr_to_print_sum, &foo, 95, _1);
        f4(5);
     
        std::cout << "7) bind to a pointer to data member: ";
        auto f5 = std::bind(&Foo::data, _1);
        std::cout << f5(foo) << '\n';
     
        std::cout << "8) bind to a mem_fn that is a pointer to data member: ";
        auto ptr_to_data = std::mem_fn(&Foo::data);
        auto f6 = std::bind(ptr_to_data, _1);
        std::cout << f6(foo) << '\n';
     
        std::cout << "9) use smart pointers to call members of the referenced objects: ";
        std::cout << f6(std::make_shared(foo)) << ' '
                  << f6(std::make_unique(foo)) << '\n';
    }

     std::bind 定义

    C++
    template
    inline 
    __bind<_Fp, _BoundArgs...>
    bind(_Fp&& __f, _BoundArgs&&... __bound_args)
    {
        typedef __bind<_Fp, _BoundArgs...> type;
        return type(_VSTD::forward<_Fp>(__f), _VSTD::forward<_BoundArgs>(__bound_args)...);
    }

    也就是说bind 定义了__bind<_Fp, _BoundArgs...> 这个是其源码的核心,搞清楚bind,只需要搞清楚__bind 的实现就可以了

    __bind 定义

    C++
    template
    class __bind
        : public __weak_result_type<typename decay<_Fp>::type>
    {
    protected:
        typedef typename decay<_Fp>::type _Fd;
        typedef tuple::type...> _Td;//decay 萃取机,萃取具体类型
    private:
        _Fd __f_;
        _Td __bound_args_;

        typedef typename __make_tuple_indices::type __indices;
    }

    可以看出来__bind 下边定义了__f_ 与 __bound_args_,__f_是记录的第一个参数的地址,__bound_args_后边跟的是参数,也就是底层支撑的数据结构是tuple,__indices记录参数的索引,后边会用的到。

    __bind operator() 定义分析

    C++
    template
    class __bind
        : public __weak_result_type::type>
    {
    protected:
        typedef typename decay<_Fp>::type _Fd;
        typedef tuple::type...> _Td;//decay 萃取机,萃取具体类型
    private:
        _Fd __f_;
        _Td __bound_args_;
    template
        _LIBCPP_INLINE_VISIBILITY
        typename __bind_return<_Fd, _Td, tuple<_Args&&...> >::type
        operator()(_Args&& ...__args)
        {
            return _VSTD::__apply_functor(__f_, __bound_args_, __indices(),
                                  tuple<_Args&&...>(_VSTD::forward<_Args>(__args)...));
        }
        typedef typename __make_tuple_indices::type __indices;
    }

    当我们调用() 方法会调用__apply_functor, 也就是说__apply_functor实现确认了bind调用的逻辑

    __apply_functor 定义

    C++
    inline _LIBCPP_INLINE_VISIBILITY
    typename __bind_return<_Fp, _BoundArgs, _Args>::type
    __apply_functor(_Fp& __f, _BoundArgs& __bound_args, __tuple_indices<_Indx...>,
                    _Args&& __args)
    {
        return _VSTD::__invoke(__f, _VSTD::__mu(_VSTD::get<_Indx>(__bound_args), __args)...);
    }

    最终调用了invoke ,_VSTD::__mu(_VSTD::get<_Indx>(__bound_args), __args) 这个实现很关键,placeholder里边的具体使用都在__mu 里边,在讲解__mu之前,我们先讲下placeholder 到底是什么

    std::placeholder

    C++
    namespace placeholders
    {

    template _Np> struct __ph {};

    /* _LIBCPP_INLINE_VAR */ constexpr __ph<1>   _1{};
    /* _LIBCPP_INLINE_VAR */ constexpr __ph<2>   _2{};
    /* _LIBCPP_INLINE_VAR */ constexpr __ph<3>   _3{};
    /* _LIBCPP_INLINE_VAR */ constexpr __ph<4>   _4{};
    /* _LIBCPP_INLINE_VAR */ constexpr __ph<5>   _5{};
    /* _LIBCPP_INLINE_VAR */ constexpr __ph<6>   _6{};
    /* _LIBCPP_INLINE_VAR */ constexpr __ph<7>   _7{};
    /* _LIBCPP_INLINE_VAR */ constexpr __ph<8>   _8{};
    /* _LIBCPP_INLINE_VAR */ constexpr __ph<9>   _9{};
    /* _LIBCPP_INLINE_VAR */ constexpr __ph<10> _10{};


    }  // placeholders

        看的出来std::placeholder::_1 其实就是__ph<1> ,有了这个介绍,再回到上边看下_VSTD::get<_Indx>(__bound_args), __args) 是干什么用的

    _VSTD::get<_Indx>(__bound_args), __args)

    C++
    inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
    typename tuple_element<_Ip, tuple<_Tp...> >::type&
    get(tuple<_Tp...>& __t) _NOEXCEPT
    {
        typedef _LIBCPP_NODEBUG_TYPE typename tuple_element<_Ip, tuple<_Tp...> >::type type;
        return static_cast<__tuple_leaf<_Ip, type>&>(__t.__base_).get();
    }

    这个模版方法,会从__bound_args 0, 1,2,3,4,5,一个一个取出来,然后就到了_VSTD::__mu实现了

    __mu

    C++
    template
    inline _LIBCPP_INLINE_VISIBILITY
    _Tp&
    __mu(reference_wrapper<_Tp> __t, _Uj&)
    {
        return __t.get();
    }

    template _Indx>
    inline _LIBCPP_INLINE_VISIBILITY
    typename __invoke_of<_Ti&, _Uj...>::type
    __mu_expand(_Ti& __ti, tuple<_Uj...>& __uj, __tuple_indices<_Indx...>)
    {
        return __ti(_VSTD::forward<_Uj>(_VSTD::get<_Indx>(__uj))...);
    }

    template
    inline _LIBCPP_INLINE_VISIBILITY
    typename _EnableIf
    <
        is_bind_expression<_Ti>::value,
        __invoke_of<_Ti&, _Uj...>
    >::type
    __mu(_Ti& __ti, tuple<_Uj...>& __uj)
    {
        typedef typename __make_tuple_indices::type __indices;
        return  __mu_expand(__ti, __uj, __indices());
    }

    template IsPh, class _Ti, class _Uj>
    struct __mu_return2 {};

    template
    struct __mu_return2
    {
        typedef typename tuple_element::value - 1, _Uj>::type type;
    };

    template
    inline _LIBCPP_INLINE_VISIBILITY
    typename enable_if
    <
        0 < is_placeholder<_Ti>::value,
        typename __mu_return2<0 < is_placeholder<_Ti>::value, _Ti, _Uj>::type
    >::type
    __mu(_Ti&, _Uj& __uj)
    {
        const size_t _Indx = is_placeholder<_Ti>::value - 1;
        return _VSTD::forward::type>(_VSTD::get<_Indx>(__uj));
    }

    template
    inline _LIBCPP_INLINE_VISIBILITY
    typename enable_if
    <
        !is_bind_expression<_Ti>::value &&
        is_placeholder<_Ti>::value == 0 &&
        !__is_reference_wrapper<_Ti>::value,
        _Ti&
    >::type
    __mu(_Ti& __ti, _Uj&)
    {
        return __ti;
    }

    __mu 是及其复杂,他的目的就是根据是否包含placeholder 跟不同的参数,按照index 重新组装成一个参数列表, is_placeholder<_Ti>::value - 1; 是获取placeholder参数对应的id,后边_VSTD::forward::type>(_VSTD::get<_Indx>(__uj));就会跟据id 构造出tuple_element 插入到参数列表中去,这就是可以做到调整参数的原因,很是巧妙

  • 相关阅读:
    wireshark使用host文件做IP域名解析
    在很多公司里面会使用打tag的方式保留版本
    oracle数据导出exp导入imp
    springboot注解之@ConfigurationProperties注解
    南大通用数据库-Gbase-8a-学习-04-部署分布式集群
    Lingolingo
    数仓面经大框架
    Hadoop学习记录5--YARN学习1
    【优化调度】基于NSGAII算法的车辆充电调度策略研究含Matlab代码
    硬核实力!飞凌 TI Sitara AM62X 系列-335x经典再续
  • 原文地址:https://blog.csdn.net/c553110519/article/details/126472819