Std::function и std::bind: что это такое и когда их следует использовать?
Я знаю, что такое функторы и когда их использовать с алгоритмами std
, но я не понял, что говорит о них Страуструп в С++ 11 FAQ.
Кто-нибудь может объяснить, что такое std::bind
и std::function
, когда их следует использовать, и привести несколько примеров для новичков?
Ответы
Ответ 1
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
(он использует оператор <=
, а не <
), поэтому, если они не согласованы, вам может понадобиться это, и вам также может понадобиться посетить автор класса с cluestick). Это своего рода преобразование, которое появляется, если вы используете функциональный стиль программирования.
Ответ 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);
}
};
Ответ 3
std:: bind был проголосован в библиотеку после предложения включить boost bind, в первую очередь это специализация частичной функции, где вы можете исправить несколько параметров и изменить другие на лету. Теперь это библиотечный способ делать лямбды в С++. Как ответил Стив Джессоп
Теперь, когда С++ 11 поддерживает лямбда-функции, я больше не испытываю соблазна использовать std:: bind. Я предпочел бы использовать currying (частичная специализация) с функцией языка, кроме функции библиотеки.
std:: функции - это полиморфные функции. Основная идея заключается в том, чтобы иметь возможность ссылаться на все вызываемые объекты взаимозаменяемо.
Я бы указал вам на эти две ссылки для получения дополнительной информации:
Лямбда-функции в С++ 11:
http://www.nullptr.me/2011/10/12/c11-lambda-having-fun-with-brackets/#.UJmXu8XA9Z8
Вызываемый объект в С++:
http://www.nullptr.me/2011/05/31/callable-entity/#.UJmXuMXA9Z8
Ответ 4
Я долго использовал его для создания пула потоков плагинов в С++; Поскольку функция принимала три параметра, вы можете написать вот так:
Предположим, что ваш метод имеет подпись:
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/A-C-Plug-in-ThreadPool-Design