Function 和 std: : bind: 它们是什么,什么时候应该使用它们?

我知道什么是函数,以及什么时候在 std算法中使用它们,但是我还不明白 Stroustrup 在 C + + 11常见问题解答中是怎么说它们的。

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

126939 次浏览

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(它使用的是 <=操作符而不是 <,所以如果它们不一致,那么您可能需要这个例子,并且您可能还需要访问类的作者与 cluestk)。不过,如果使用函数式编程风格,就会出现这种转换。

Bind 在被提议包含 Bostbind 之后被投票加入到库中,主要是部分函数专门化,在这里你可以修复一些参数并动态地修改其他参数。这是在 C + + 中实现 lambdas 的库方法。正如 Steve Jessop 所回答的

既然 C + + 11支持 lambda 函数,我就不再想使用 std: : bind 了。我宁愿使用带有语言特性的套用(部分专门化)而不是库特性。

函数对象是多态函数,其基本思想是能够互换地引用所有可调用的对象。

我会向你指出这两个链接的进一步细节:

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/

我很久以前就用它在 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的 helper 函数:

//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-> Three Parameter terTask (21,22,23) ;

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

函数和 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);
}
};