163

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

任何人都可以解释什么std::bindstd::function是什么,什么时候应该使用它们,并为新手举一些例子?

4

4 回答 4

240

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

也就是说,假设您有一个带有f3 个参数的函数对象:

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

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

于 2011-07-07T11:54:04.340 回答
43

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);
    }
};
于 2016-12-03T05:28:27.077 回答
14

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/

于 2012-11-06T23:06:07.463 回答
6

我很久以前用它在 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;
};

并且,一个使用 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/AC-Plug-in-ThreadPool-Design

于 2014-02-14T06:00:41.663 回答