【问题标题】:std::function and std::bind: what are they, and when should they be used?std::function 和 std::bind: 它们是什么,应该在什么时候使用它们?
【发布时间】:2011-09-30 09:40:42
【问题描述】:

我知道函子是什么以及何时将它们与std 算法一起使用,但我不明白 Stroustrup 在C++11 FAQ 中对它们的看法。

谁能解释一下std::bindstd::function 是什么,什么时候应该使用它们,并为新手举一些例子?

【问题讨论】:

    标签: c++ c++11 std-function stdbind


    【解决方案1】:

    std::bind 用于partial function application

    也就是说,假设您有一个函数对象 f,它接受 3 个参数:

    f(a,b,c);
    

    你想要一个新的函数对象,它只接受两个参数,定义为:

    g(a,b) := f(a, 4, b);
    

    g是函数f的“部分应用”:中间的参数已经指定,还有两个要走。

    您可以使用std::bind获取g

    auto g = bind(f, _1, 4, _2);
    

    这比实际编写一个仿函数类更简洁。

    您链接到的文章中还有更多示例。当您需要将仿函数传递给某个算法时,通常会使用它。您有一个函数或仿函数,几乎可以完成您想要的工作,但比算法使用的更可配置(即具有更多参数)。所以你将参数绑定到一些参数,剩下的留给算法来填充:

    // raise every value in vec to the power of 7
    std::transform(vec.begin(), vec.end(), some_output, std::bind(std::pow, _1, 7));
    

    这里,pow 有两个参数,可以提升到任何次幂,但我们关心的只是提升到 7 的次方。

    作为非偏函数应用的偶尔使用,bind 也可以对函数的参数重新排序:

    auto memcpy_with_the_parameters_in_the_right_flipping_order = bind(memcpy, _2, _1, _3);
    

    我不建议仅仅因为你不喜欢这个 API 就使用它,但它有潜在的实际用途,例如因为:

    not2(bind(less<T>, _2, _1));
    

    是一个小于或等于函数(假设一个总顺序,等等等等)。这个例子通常是不必要的,因为已经有一个std::less_equal(它使用&lt;= 运算符而不是&lt;,所以如果它们不一致,那么你可能需要这个,你可能还需要访问带有线索的班级的作者)。不过,如果您使用的是函数式编程风格,就会出现这种转换。

    【讨论】:

    • 对于成员函数的回调也很方便:myThread=boost::thread(boost::bind(&amp;MyClass::threadMain, this))
    • 对绑定的很好解释。但是std::function呢?
    • 您的 pow 示例无法编译。由于pow 是一个重载函数,因此您必须手动指定哪个重载。绑定不能让它由结果函子的调用者推断。例如。 std::transform(vec.begin(), vec.end(), out.begin(), std::bind((double (*)(double, int))std::pow, _1, 7));
    • 很好解释,但有时std::bindthis 用法一起作为第二个参数。你能详细说明一下这个用例吗?
    • 另外,“_1”是指std::placeholders::_1。我花了一段时间才知道为什么这没有编译。
    【解决方案2】:

    std::function 和 std::bind 的主要用途之一是作为更通用的函数指针。您可以使用它来实现回调机制。一种流行的情况是,您有一些需要很长时间才能执行的函数,但您不想等待它返回,然后您可以在单独的线程上运行该函数并给它一个函数指针,它将完成后回调。

    下面是如何使用它的示例代码:

    class MyClass {
    private:
        //just shorthand to avoid long typing
        typedef std::function<void (float result)> TCallback;
    
        //this function takes long time
        void longRunningFunction(TCallback callback)
        {
            //do some long running task
            //...
            //callback to return result
            callback(result);
        }
    
        //this function gets called by longRunningFunction after its done
        void afterCompleteCallback(float result)
        {
            std::cout << result;
        }
    
    public:
        int longRunningFunctionAsync()
        {
            //create callback - this equivalent of safe function pointer
            auto callback = std::bind(&MyClass::afterCompleteCallback, 
                this, std::placeholders::_1);
    
            //normally you want to start below function on seprate thread, 
            //but for illustration we will just do simple call
            longRunningFunction(callback);
        }
    };
    

    【讨论】:

    • 这是一个很好的答案。我已经四处寻找这个答案。谢谢@ShitalShah
    • 您能否解释一下为什么绑定有助于使其更安全?
    • 我的错……我并不是说它更“安全”。普通函数指针也是类型安全的,但是 std::function 对于 lambdas、上下文捕获、成员方法等更通用。
    • bind(&MyClass::afterCompleteCallback, this, std::placeholders::_1) , 2 args for 1 in definition, void afterCompleteCallback(float result) ,能解释一下吗?
    • @nonock 对于成员函数的函数指针,我们需要传递“this”指针作为第一个参数。
    【解决方案3】:

    std::bind 在提议包含 boost 绑定后被投票加入库,主要是部分函数专业化,您可以在其中修复一些参数并即时更改其他参数。现在这是在 C++ 中执行 lambda 的库方式。正如史蒂夫·杰索普所回答的那样

    现在 C++11 支持 lambda 函数,我不再想使用 std::bind 了。我宁愿使用带有语言功能的柯里化(部分专业化)而不是库功能。

    std::function 对象是多态函数。基本思想是能够互换地引用所有可调用对象。

    我会向您指出这两个链接以获取更多详细信息:

    C++11 中的 Lambda 函数: https://www.variadic.xyz/2011/10/12/c11-lambda-having-fun-with-brackets/

    C++ 中的可调用实体: https://www.variadic.xyz/2011/05/31/callable-entity/

    【讨论】:

    • std::bind 在没有 lambda 的情况下永远不会存在——这两个特性都是在 C++11 中引入的。我们确实有 bind1stbind2nd,它们是 C++11 绑定的瘦弱版本。
    • nullptr.me 域不再存在。
    【解决方案4】:

    我很久以前就用它在 C++ 中创建插件线程池;由于该函数采用三个参数,因此您可以这样编写

    假设你的方法有签名:

    int CTask::ThreeParameterTask(int par1, int par2, int par3)
    

    要创建一个函数对象来绑定三个参数,你可以这样做

    // a template class for converting a member function of the type int function(int,int,int)
    //to be called as a function object
    template<typename _Ret,typename _Class,typename _arg1,typename _arg2,typename _arg3>
    class mem_fun3_t
    {
    public:
        explicit mem_fun3_t(_Ret (_Class::*_Pm)(_arg1,_arg2,_arg3))
            :m_Ptr(_Pm) //okay here we store the member function pointer for later use
        {}
    
        //this operator call comes from the bind method
        _Ret operator()(_Class *_P, _arg1 arg1, _arg2 arg2, _arg3 arg3) const
        {
            return ((_P->*m_Ptr)(arg1,arg2,arg3));
        }
    private:
        _Ret (_Class::*m_Ptr)(_arg1,_arg2,_arg3);// method pointer signature
    };
    

    现在,为了绑定参数,我们必须编写一个 binder 函数。所以,就这样吧:

    template<typename _Func,typename _Ptr,typename _arg1,typename _arg2,typename _arg3>
    class binder3
    {
    public:
        //This is the constructor that does the binding part
        binder3(_Func fn,_Ptr ptr,_arg1 i,_arg2 j,_arg3 k)
            :m_ptr(ptr),m_fn(fn),m1(i),m2(j),m3(k){}
    
    
            //and this is the function object 
            void operator()() const
            {
                m_fn(m_ptr,m1,m2,m3);//that calls the operator
            }
    private:
        _Ptr m_ptr;
        _Func m_fn;
        _arg1 m1; _arg2 m2; _arg3 m3;
    };
    

    还有一个使用 binder3 类的辅助函数 - bind3:

    //a helper function to call binder3
    template <typename _Func, typename _P1,typename _arg1,typename _arg2,typename _arg3>
    binder3<_Func, _P1, _arg1, _arg2, _arg3> bind3(_Func func, _P1 p1,_arg1 i,_arg2 j,_arg3 k)
    {
        return binder3<_Func, _P1, _arg1, _arg2, _arg3> (func, p1,i,j,k);
    }
    

    这里我们如何称呼它

    F3 f3 = PluginThreadPool::bind3( PluginThreadPool::mem_fun3( 
              &CTask::ThreeParameterTask), task1,2122,23 );
    

    注意:f3();会调用方法task1->ThreeParameterTask(21,22,23);

    更多血腥细节 --> http://www.codeproject.com/Articles/26078/A-C-Plug-in-ThreadPool-Design

    【讨论】:

      猜你喜欢
      • 2011-03-25
      • 1970-01-01
      • 1970-01-01
      • 2021-10-02
      • 2010-11-01
      • 1970-01-01
      • 2017-03-23
      相关资源
      最近更新 更多