std :: function e std :: bind: o que são e quando devem ser usados?

127

Sei o que são functores e quando usá-los com stdalgoritmos, mas não entendi o que o Stroustrup diz sobre eles no FAQ do C ++ 11 .

Alguém pode explicar o que std::binde std::functionsão, quando eles devem ser usados, e dar alguns exemplos para iniciantes?

Mr.Anubis
fonte

Respostas:

200

std::bindé para aplicação de função parcial .

Ou seja, suponha que você tenha um objeto de função fque use 3 argumentos:

f(a,b,c);

Você deseja um novo objeto de função que use apenas dois argumentos, definidos como:

g(a,b) := f(a, 4, b);

gé uma "aplicação parcial" da função f: o argumento do meio já foi especificado e há dois a serem deixados.

Você pode usar std::bindpara obter g:

auto g = bind(f, _1, 4, _2);

Isso é mais conciso do que realmente escrever uma classe functor para fazê-lo.

Existem outros exemplos no artigo ao qual você vincula. Você geralmente o usa quando precisa passar um functor para algum algoritmo. Você tem uma função ou functor que quase faz o trabalho que deseja, mas é mais configurável (ou seja, possui mais parâmetros) do que o algoritmo usa. Então, você vincula argumentos a alguns dos parâmetros e deixa o restante para o algoritmo preencher:

// raise every value in vec to the power of 7
std::transform(vec.begin(), vec.end(), some_output, std::bind(std::pow, _1, 7));

Aqui, são pownecessários dois parâmetros e podem aumentar para qualquer potência, mas tudo o que importa é elevar a potência de 7.

Como uso ocasional que não é aplicação parcial de função, bindtambém é possível reordenar os argumentos para uma função:

auto memcpy_with_the_parameters_in_the_right_flipping_order = bind(memcpy, _2, _1, _3);

Não recomendo usá-lo apenas porque você não gosta da API, mas ela tem possíveis usos práticos, por exemplo, porque:

not2(bind(less<T>, _2, _1));

é uma função menor ou igual (assumindo uma ordem total, blá blá). Esse exemplo normalmente não é necessário, pois já existe um std::less_equal(ele usa o <=operador e não <, portanto, se eles não são consistentes, você pode precisar disso e também visitar o autor da classe com um cluestick). É o tipo de transformação que surge se você estiver usando um estilo funcional de programação.

Steve Jessop
fonte
17
Também útil para retornos de chamada para funções membro:myThread=boost::thread(boost::bind(&MyClass::threadMain, this))
rlduffy
15
Boa explicação do bind. Mas que tal std::function?
RedX 03/04/12
10
Seu powexemplo não é compilado. Como powé uma função sobrecarregada, é necessário especificar manualmente qual sobrecarga. A ligação não pode ser deduzida pelo responsável pela chamada do functor resultante. Exemplostd::transform(vec.begin(), vec.end(), out.begin(), std::bind((double (*)(double, int))std::pow, _1, 7));
MM
2
Muito bem explicado, mas às vezes std::bindvem junto com o thisuso como o segundo argumento. Você pode elaborar esse caso de uso?
Mendes
2
Também com "_1" você quer dizer std::placeholders::_1. Demorei um pouco para descobrir por que isso não estava sendo compilado.
terryg
24

Um dos principais usos de std :: function e std :: bind é como ponteiros de função mais generizados. Você pode usá-lo para implementar o mecanismo de retorno de chamada. Um dos cenários populares é que você tem alguma função que levará muito tempo para ser executada, mas não deseja esperar que ela retorne. Você pode executar essa função em um thread separado e fornecer um ponteiro de função que retorno de chamada após a conclusão.

Aqui está um código de exemplo de como usar isso:

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);
    }
};
Shital Shah
fonte
5
Esta é uma ótima resposta. Eu procurei por toda parte para encontrar esta resposta. Obrigado @ShitalShah
terryg
Você poderia adicionar uma explicação sobre por que a ligação ajuda a torná-la mais segura?
Steven Lu
Meu mal ... eu não pretendia dizer que é mais "seguro". Ponteiros de função normais também são typesafe no entanto std :: função é mais genérico para o trabalho com lambdas, captura de contexto, métodos de membros etc.
Shital Shah
bind (& MyClass :: afterCompleteCallback, this, std :: placeholders :: _ 1), 2 args para 1 na definição, void afterCompleteCallback (resultado flutuante), pode explicar isso?
nonock
1
@nonock Para ponteiros de função de funções membro, precisamos passar o ponteiro "this" como o primeiro argumento.
sanoj subran 18/07
12

std :: bind foi votado na biblioteca após a proposta para incluir o boost bind, principalmente é a especialização parcial da função, onde é possível corrigir alguns parâmetros e alterar outros rapidamente. Agora, essa é a maneira da biblioteca fazer lambdas em C ++. Respondida por Steve Jessop

Agora que o C ++ 11 suporta funções lambda, não sinto mais nenhuma tentação de usar std :: bind. Prefiro usar currying (especialização parcial) com recurso de idioma do que com recurso de biblioteca.

Os objetos std :: function são funções polimórficas. A idéia básica é ser capaz de se referir a todos os objetos que podem ser chamados de forma intercambiável.

Eu indicaria esses dois links para mais detalhes:

Funções Lambda no C ++ 11: http://www.nullptr.me/2011/10/12/c11-lambda-having-fun-with-brackets/#.UJmXu8XA9Z8

Entidade que pode ser chamada em C ++: http://www.nullptr.me/2011/05/31/callable-entity/#.UJmXuMXA9Z8

Sarang
fonte
5
std::bindnunca existiu sem lambdas - esses dois recursos foram introduzidos no C ++ 11. Nós tínhamos bind1ste bind2ndquais eram as versões emaciadas do C ++ 11 bind.
30715 MM
5

Usei muito tempo para criar um pool de threads de plug-in em C ++; Como a função estava usando três parâmetros, você pode escrever assim

Suponha que seu método tenha a assinatura:

int CTask::ThreeParameterTask(int par1, int par2, int par3)

Para criar um objeto de função para ligar os três parâmetros, você pode fazer assim

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

Agora, para vincular os parâmetros, precisamos escrever uma função do fichário. Então, aqui vai:

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

E, uma função auxiliar para usar a classe 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);
}

e aqui nos como chamá-lo

F3 f3 = PluginThreadPool::bind3( PluginThreadPool::mem_fun3( 
          &CTask::ThreeParameterTask), task1,2122,23 );

Nota: f3 (); chamará o método task1-> ThreeParameterTask (21,22,23);

Para obter detalhes mais sangrentos -> http://www.codeproject.com/Articles/26078/AC-Plug-in-ThreadPool-Design

Alex Punnen
fonte