std :: function et std :: bind: que sont-ils et quand doivent-ils être utilisés?

129

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

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

Monsieur Anubis
la source

Réponses:

201

std::bindest pour une application de fonction partielle .

Autrement dit, supposons que vous ayez un objet fonction fqui prend 3 arguments:

f(a,b,c);

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

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

gest 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::bindpour 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 fait presque le travail que vous voulez, mais qui est plus configurable (c'est-à-dire a plus de paramètres) que l'algorithme n'en utilise. Donc, vous liez des arguments à certains des paramètres et laissez le reste pour l'algorithme à remplir:

// 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, powprend deux paramètres et peut s'élever à n'importe quelle puissance, mais tout ce qui nous importe est de monter à la puissance de 7.

En tant qu'utilisation occasionnelle qui n'est pas une application de fonction partielle, bindpeut é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 parce que:

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 l' <=opérateur plutôt que <, donc s'ils ne sont pas cohérents, vous pourriez en avoir besoin, et vous devrez peut-être également visiter l'auteur de la classe avec un clic). C'est le genre de transformation qui survient si vous utilisez un style de programmation fonctionnel.

Steve Jessop
la source
18
Aussi pratique pour les rappels aux fonctions membres:myThread=boost::thread(boost::bind(&MyClass::threadMain, this))
rlduffy
15
Belle explication de bind. Mais qu'en est-il std::function?
RedX
10
Votre powexemple ne compile pas. Puisqu'il pows'agit d'une fonction surchargée, vous devez spécifier manuellement quelle surcharge. La liaison ne peut pas laisser être déduite par l'appelant du foncteur résultant. Par exemplestd::transform(vec.begin(), vec.end(), out.begin(), std::bind((double (*)(double, int))std::pow, _1, 7));
MM
2
Très bien expliqué, mais std::binds'accompagne parfois de l' thisutilisation comme 2ème argument. Pouvez-vous s'il vous plaît élaborer ce cas d'utilisation?
Mendes
2
Vous entendez également par "_1" std::placeholders::_1. Il m'a fallu un certain temps pour savoir pourquoi cela ne compilait pas.
terryg
26

L'une des principales utilisations de std :: function et std :: bind est l'utilisation de pointeurs de fonctions plus généralisés. Vous pouvez l'utiliser pour implémenter un mécanisme de rappel. L'un des scénarios les plus courants est que vous avez une fonction qui prendra beaucoup de temps à s'exécuter mais que vous ne voulez pas attendre son retour, vous pouvez alors exécuter cette fonction sur un thread séparé et lui donner un pointeur de fonction indiquant qu'elle callback une fois terminé.

Voici un exemple de code pour savoir comment 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);
    }
};
Shital Shah
la source
5
C'est une excellente réponse. J'ai cherché partout pour trouver cette réponse. Merci @ShitalShah
terryg
Pouvez-vous expliquer pourquoi la reliure contribue à la rendre plus sûre?
Steven Lu
Mon mauvais ... Je n'avais pas l'intention de dire que c'est plus "plus sûr". Les pointeurs de fonction normaux sont également de type sécurisé, mais std :: function est plus générique pour fonctionner avec les lambdas, la capture de contexte, les méthodes membres, etc.
Shital Shah
bind (& MyClass :: afterCompleteCallback, this, std :: placeholders :: _ 1), 2 arguments pour 1 dans la définition, void afterCompleteCallback (résultat float), peut expliquer cela?
nonock le
1
@nonock Pour les pointeurs de fonction des fonctions membres, nous devons passer le pointeur "this" comme premier argument.
sanoj subran
12

std :: bind a été voté dans la bibliothèque après la proposition d'inclure boost bind, principalement il s'agit d'une spécialisation partielle des fonctions dans laquelle vous pouvez corriger quelques paramètres et en changer d'autres à la volée. Maintenant, c'est une méthode de bibliothèque pour faire des lambdas en C ++. Réponse de Steve Jessop

Maintenant que C ++ 11 prend en charge les fonctions lambda, je ne ressens plus aucune tentation d'utiliser std :: bind. Je préférerais utiliser le curry (spécialisation partielle) avec la fonction de langue plutôt que la 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 indiquerai 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

Sarang
la source
5
std::bindn'a jamais existé sans lambdas - ces deux fonctionnalités ont été introduites dans C ++ 11. Nous avons eu bind1stet bind2ndqui étaient des versions émaciées de C ++ 11 bind.
MM
5

Je l'ai utilisé il y a longtemps pour créer un pool de threads de plugins en C ++; Puisque la fonction prenait trois paramètres, vous pouvez écrire comme ceci

Supposons que votre méthode ait la signature:

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

Pour créer un objet fonction pour lier les trois paramètres, vous pouvez faire comme ceci

// 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, pour lier les paramètres, nous devons écrire une fonction de classeur. 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/AC-Plug-in-ThreadPool-Design

Alex Punnen
la source