std :: function и std :: bind: что это такое и когда их следует использовать? - PullRequest
109 голосов
/ 07 июля 2011

Я знаю, что такое функторы и когда их использовать с std алгоритмами, но я не понял, что говорит о них Страуструп в C ++ 11 FAQ .

Кто-нибудь может объяснить, что такое std::bind и std::function, когда их следует использовать, и привести несколько примеров для новичков?

Ответы [ 4 ]

171 голосов
/ 07 июля 2011

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 (он использует оператор <= вместо <, поэтому, если они не согласованы, вам может понадобиться это, и вам также может понадобиться посетитьавтор класса с ключиком).Это тот вид трансформации, который возникает, если вы используете функциональный стиль программирования.

12 голосов
/ 07 ноября 2012

std :: bind был принят в библиотеку после того, как было предложено включить boost bind, в первую очередь это частичная специализация функций, в которой вы можете исправить несколько параметров и изменить другие на лету.Теперь это библиотечный способ делать лямбды в C ++.Как ответил Стив Джессоп

Теперь, когда C ++ 11 поддерживает лямбда-функции, я больше не испытываю соблазна использовать std :: bind.Я бы предпочел использовать каррирование (частичную специализацию) с возможностью языка, а не библиотекой.

std :: function объекты являются полиморфными функциями.Основная идея в том, чтобы иметь возможность ссылаться на все вызываемые объекты взаимозаменяемо.

Я хотел бы указать вам эти две ссылки для получения дополнительной информации:

Лямбда-функции в C ++ 11: 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

11 голосов
/ 03 декабря 2016

Одним из основных способов использования 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 голосов
/ 14 февраля 2014

Я использовал это давно, чтобы создать пул потоков плагинов в 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/A-C-Plug-in-ThreadPool-Design

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...