web-dev-qa-db-fra.com

std :: function et std :: bind: de quoi s'agit-il et quand doivent-ils être utilisés?

Je sais ce que sont les foncteurs et quand les utiliser avec std algorithmes, mais je n'ai pas compris ce que Stroustrup en dit dans le FAQ C++ 11 .

Quelqu'un peut-il expliquer quoi std::bind et std::function sont, quand ils devraient être utilisés, et donner quelques exemples pour les débutants?

110
Mr.Anubis

std::bind est pour application de fonction partielle .

En d'autres termes, supposons que vous ayez un objet fonction f qui prend 3 arguments:

f(a,b,c);

Vous voulez un nouvel objet fonction qui ne prend que deux arguments, définis comme suit:

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

g est une "application partielle" de la fonction f: l'argument du milieu a déjà été spécifié et il en reste deux.

Vous pouvez utiliser std::bind pour obtenir g:

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

C'est plus concis que d'écrire une classe de foncteurs pour le faire.

Il existe d'autres exemples dans l'article auquel vous créez un lien. Vous l'utilisez généralement lorsque vous devez passer un foncteur à un algorithme. Vous avez une fonction ou un foncteur qui presque fait le travail que vous voulez, mais est plus configurable (c’est-à-dire qu’il a plus de paramètres) que l’algorithme. Vous liez donc des arguments à certains paramètres et laissez le reste à l'algorithme à renseigner:

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

Ici, pow prend deux paramètres et peut élever à toute puissance, mais tout ce qui nous intéresse est d’augmenter à la puissance de 7.

En tant qu'utilisation occasionnelle qui n'est pas une application de fonction partielle, bind peut également réorganiser les arguments d'une fonction:

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

Je ne recommande pas de l'utiliser simplement parce que vous n'aimez pas l'API, mais elle a des utilisations pratiques potentielles, par exemple, pour les raisons suivantes:

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

est une fonction inférieure à ou égale (en supposant un ordre total, bla bla). Cet exemple n’est normalement pas nécessaire car il existe déjà un std::less_equal _ (il utilise le <= opérateur plutôt que <, donc si elles ne sont pas cohérentes, vous pourriez en avoir besoin et vous devrez peut-être aussi rendre visite à l'auteur de la classe avec un click). Cependant, c'est le type de transformation qui se produit si vous utilisez un style de programmation fonctionnel.

172
Steve Jessop

std :: bind a été voté dans la bibliothèque après la proposition d'inclure boost bind, il s'agit principalement d'une spécialisation de fonction partielle dans laquelle vous pouvez modifier quelques paramètres et en modifier d'autres à la volée. Maintenant, c’est la façon dont les bibliothèques utilisent lambdas en C++. Comme l'a répondu Steve Jessop

Maintenant que C++ 11 supporte les fonctions lambda, je ne ressens plus aucune tentation d'utiliser std :: bind. Je préférerais utiliser currying (spécialisation partielle) avec fonction de langage plutôt que fonction de bibliothèque.

les objets std :: function sont des fonctions polymorphes. L'idée de base est de pouvoir faire référence à tous les objets appelables de manière interchangeable.

Je vous indiquerais ces deux liens pour plus de détails:

Fonctions Lambda en C++ 11: http://www.nullptr.me/2011/10/12/c11-lambda-having-fun-with-brackets/#.UJmXu8XA9Z8

Entité appelable en C++: http://www.nullptr.me/2011/05/31/callable-entity/#.UJmXuMXA9Z8

12
Sarang

L'une des utilisations principales de std :: function et de std :: bind est celle d'indicateurs de fonction sécurisés. Vous pouvez l'utiliser pour implémenter un mécanisme de rappel. L'un des scénarios populaires est que vous avez une fonction qui va prendre du temps à s'exécuter mais vous ne voulez pas attendre qu'elle revienne, vous pouvez alors l'exécuter sur un thread séparé et lui donner un pointeur de fonction rappel après qu'il se termine.

Voici un exemple de code pour utiliser ceci:

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);
    }
};
12
Shital Shah

Je l’utilisais depuis longtemps pour créer un pool de threads de plugins en C++; Comme la fonction prenait trois paramètres, vous pouvez écrire comme ceci

Supposons que votre méthode possède la signature:

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

Pour créer un objet fonction pour lier les trois paramètres, procédez comme suit:

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

Maintenant, afin de lier les paramètres, nous devons écrire une fonction de liant. Alors, voilà:

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

Et, une fonction d'assistance pour utiliser la 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);
}

et ici nous comment l'appeler

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

Remarque: f3 (); appellera la méthode task1-> ThreeParameterTask (21,22,23);

Pour plus de détails sanglants -> http://www.codeproject.com/Articles/26078/A-C-Plug-in-ThreadPool-Design

5
Alex Punnen