std :: function和std :: bind:它们是什么,何时使用?


127

我知道函子是什么以及何时将其与std算法一起使用,但是我不了解Stroustrup在C ++ 11 FAQ中对函子的评价。

任何人都可以解释什么std::bindstd::function是,当他们要使用,并给新手一些例子吗?

Answers:


200

std::bind适用于部分功能应用

也就是说,假设您有一个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(它使用<=运算符而不是<,因此,如果它们不一致,则可能需要使用此操作,并且可能还需要借助提示来访问类的作者)。但是,如果您使用的是编程的功能样式,则这是一种转换。


17
也方便用于成员函数的回调:myThread=boost::thread(boost::bind(&MyClass::threadMain, this))
rlduffy 2011年

15
绑定的很好的解释。但是呢std::function
RedX 2012年

10
您的pow示例无法编译。由于pow是重载函数,因此您必须手动指定哪个重载。绑定不能让它由结果函子的调用者推断。EGstd::transform(vec.begin(), vec.end(), out.begin(), std::bind((double (*)(double, int))std::pow, _1, 7));
MM

2
解释得很好,但有时std::bindthis用法一起作为第二个参数。您能否详细说明该用例?
Mendes

2
同样用“ _1”表示std::placeholders::_1。花了我一段时间来找出为什么没有编译。
terryg

24

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);
    }
};

5
这是一个很好的答案。我到处都在寻找这个答案。感谢@ShitalShah
terryg

您能解释一下为什么绑定有助于使其更安全吗?
史蒂文·卢

我的坏...我没打算说它更“安全”。普通函数指针也不过类型安全标准::功能是更通用与lambda表达式的工作,背景拍摄,成员方法等
清淳沙阿

bind(&MyClass :: afterCompleteCallback,this,std :: placeholders :: _ 1),2个args定义为1,void afterCompleteCallback(float result)可以解释吗?
诺诺克

1
@nonock对于成员函数的函数指针,我们需要传递“ this”指针作为第一个参数。
sanoj subran

12

建议将std :: bind选入库后,建议包含boost绑定,主要是部分功能专门化,您可以在其中固定几个参数,并即时更改其他参数。现在,这是在C ++中执行lambda的库方法。正如史蒂夫·杰索普(Steve Jessop)的回答

既然C ++ 11支持lambda函数,我再也不会想要使用std :: bind了。我宁愿使用具有语言功能的currying(部分专业化)功能,而不要使用库功能。

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

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

C ++ 11中的Lambda函数:http//www.nullptr.me/2011/10/12/c11-lambda-having-fun-with-brackets/#.UJmXu8XA9Z8

C ++中的可调用实体:http : //www.nullptr.me/2011/05/31/callable-entity/#.UJmXuMXA9Z8


5
std::bind没有lambda便永远不会存在-这两个功能都是C ++ 11中引入的。我们确实有bind1stbind2ndC ++ 11绑定的瘦弱版本。
MM

5

我花了很长时间回到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
};

现在,为了绑定参数,我们必须编写一个绑定函数。因此,它去了:

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;
};

并且,一个辅助函数可以使用活页夹3类-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/AC-Plug-in-ThreadPool-Design

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.