web-dev-qa-db-fra.com

Que sont les foncteurs C ++ et leurs utilisations?

J'entends beaucoup parler de foncteurs en C++. Quelqu'un peut-il me donner un aperçu de ce qu'ils sont et dans quels cas ils seraient utiles?

811
Konrad

Un foncteur est à peu près juste une classe qui définit l'opérateur (). Cela vous permet de créer des objets qui "ressemblent" à une fonction:

// this is a functor
struct add_x {
  add_x(int x) : x(x) {}
  int operator()(int y) const { return x + y; }

private:
  int x;
};

// Now you can use it like this:
add_x add42(42); // create an instance of the functor class
int i = add42(8); // and "call" it
assert(i == 50); // and it added 42 to its argument

std::vector<int> in; // assume this contains a bunch of values)
std::vector<int> out(in.size());
// Pass a functor to std::transform, which calls the functor on every element 
// in the input sequence, and stores the result to the output sequence
std::transform(in.begin(), in.end(), out.begin(), add_x(1)); 
assert(out[i] == in[i] + 1); // for all i

Il y a quelques bonnes choses à propos des foncteurs. La première est que, contrairement aux fonctions habituelles, elles peuvent contenir des états. L'exemple ci-dessus crée une fonction qui ajoute 42 à tout ce que vous lui donnez. Mais cette valeur 42 n'est pas codée en dur, elle a été spécifiée en tant qu'argument de constructeur lors de la création de notre instance de foncteur. Je pourrais créer un autre additionneur, qui a ajouté 27, simplement en appelant le constructeur avec une valeur différente. Cela les rend joliment personnalisables.

Comme le montrent les dernières lignes, vous passez souvent des foncteurs comme arguments à d'autres fonctions telles que std :: transform ou les autres algorithmes de bibliothèque standard. Vous pouvez faire la même chose avec un pointeur de fonction normal, sauf que, comme je l'ai dit plus haut, les foncteurs peuvent être "personnalisés" car ils contiennent des états, ce qui les rend plus flexibles (si je voulais utiliser un pointeur de fonction, je devrais écrire une fonction qui a ajouté exactement 1 à son argument. Le foncteur est général, et ajoute ce que vous avez initialisé avec), et ils sont aussi potentiellement plus efficaces. Dans l'exemple ci-dessus, le compilateur sait exactement quelle fonction std::transform devrait appeler. Il devrait appeler add_x::operator(). Cela signifie qu'il peut intégrer cet appel de fonction. Et cela le rend aussi efficace que si j'avais appelé manuellement la fonction sur chaque valeur du vecteur.

Si j'avais passé un pointeur de fonction à la place, le compilateur ne pourrait pas voir immédiatement vers quelle fonction il pointe. Par conséquent, à moins d'effectuer des optimisations globales assez complexes, il devrait déréférencer le pointeur au moment de l'exécution, puis passer l'appel.

975
jalf

Petit ajout. Vous pouvez utiliser boost::function , pour créer des foncteurs à partir de fonctions et de méthodes, comme ceci:

_class Foo
{
public:
    void operator () (int i) { printf("Foo %d", i); }
};
void Bar(int i) { printf("Bar %d", i); }
Foo foo;
boost::function<void (int)> f(foo);//wrap functor
f(1);//prints "Foo 1"
boost::function<void (int)> b(&Bar);//wrap normal function
b(1);//prints "Bar 1"
_

et vous pouvez utiliser boost :: bind pour ajouter un état à ce foncteur

_boost::function<void ()> f1 = boost::bind(foo, 2);
f1();//no more argument, function argument stored in f1
//and this print "Foo 2" (:
//and normal function
boost::function<void ()> b1 = boost::bind(&Bar, 2);
b1();// print "Bar 2"
_

et le plus utile, avec boost :: bind et boost :: function vous pouvez créer un foncteur à partir de la méthode class, en fait c'est un délégué:

_class SomeClass
{
    std::string state_;
public:
    SomeClass(const char* s) : state_(s) {}

    void method( std::string param )
    {
        std::cout << state_ << param << std::endl;
    }
};
SomeClass *inst = new SomeClass("Hi, i am ");
boost::function< void (std::string) > callback;
callback = boost::bind(&SomeClass::method, inst, _1);//create delegate
//_1 is a placeholder it holds plase for parameter
callback("useless");//prints "Hi, i am useless"
_

Vous pouvez créer une liste ou un vecteur de foncteurs

_std::list< boost::function<void (EventArg e)> > events;
//add some events
....
//call them
std::for_each(
        events.begin(), events.end(), 
        boost::bind( boost::apply<void>(), _1, e));
_

Il y a un problème avec tout ça, les messages d'erreur du compilateur ne sont pas lisibles par l'homme :)

118
Evgeny Lazin

Un foncteur est un objet qui agit comme une fonction. Fondamentalement, une classe qui définit operator().

class MyFunctor
{
   public:
     int operator()(int x) { return x * 2;}
}

MyFunctor doubler;
int x = doubler(5);

Le véritable avantage est qu'un foncteur peut conserver son état.

class Matcher
{
   int target;
   public:
     Matcher(int m) : target(m) {}
     bool operator()(int x) { return x == target;}
}

Matcher Is5(5);

if (Is5(n))    // same as if (n == 5)
{ ....}
92
James Curran

Le nom "functor" a été utilisé traditionnellement dans théorie des catégories longtemps avant que C++ ne soit entré en scène. Cela n’a rien à voir avec le concept de foncteur C++. Il est préférable d'utiliser l'objet de fonction name au lieu de ce que nous appelons "foncteur" en C++. C'est ainsi que d'autres langages de programmation appellent des constructions similaires.

Utilisé à la place de la fonction plain:

Fonctionnalités:

  • L'objet de fonction peut avoir l'état
  • L'objet de fonction s'inscrit dans OOP (il se comporte comme tout autre objet).

Les inconvénients:

  • Apporte plus de complexité au programme.

Utilisé à la place du pointeur de fonction:

Fonctionnalités:

  • Les objets fonction peuvent souvent être en ligne

Les inconvénients:

  • L'objet de fonction ne peut pas être échangé avec un autre type d'objet de fonction pendant l'exécution (du moins s'il étend une classe de base, ce qui entraîne une surcharge)

Utilisé à la place de la fonction virtuelle:

Fonctionnalités:

  • L'objet de fonction (non virtuel) ne nécessite pas de distribution vtable et runtime, il est donc plus efficace dans la plupart des cas

Les inconvénients:

  • L'objet de fonction ne peut pas être échangé avec un autre type d'objet de fonction pendant l'exécution (du moins s'il étend une classe de base, ce qui entraîne une surcharge)
49
doc

Comme d'autres l'ont mentionné, un foncteur est un objet qui agit comme une fonction, c'est-à-dire qu'il surcharge l'opérateur d'appel de fonction.

Les foncteurs sont couramment utilisés dans les algorithmes STL. Ils sont utiles car ils peuvent conserver l'état avant et entre les appels de fonction, comme une fermeture dans des langages fonctionnels. Par exemple, vous pouvez définir un foncteur MultiplyBy qui multiplie son argument par un montant spécifié:

class MultiplyBy {
private:
    int factor;

public:
    MultiplyBy(int x) : factor(x) {
    }

    int operator () (int other) const {
        return factor * other;
    }
};

Ensuite, vous pouvez passer un objet MultiplyBy à un algorithme tel que std :: transform:

int array[5] = {1, 2, 3, 4, 5};
std::transform(array, array + 5, array, MultiplyBy(3));
// Now, array is {3, 6, 9, 12, 15}

Un autre avantage d'un foncteur par rapport à un pointeur vers une fonction est que l'appel peut être en ligne dans plusieurs cas. Si vous avez passé un pointeur de fonction à transform, à moins que cet appel ne soit en ligne et que le compilateur sache que vous lui transmettez toujours la même fonction, il ne peut pas insérer l'appel via le pointeur.

38
Matthew Crumley

Pour les débutants comme moi parmi nous: après une petite recherche, j'ai découvert ce que le code publié par jalf faisait.

Un foncteur est un objet class ou struct qui peut être "appelé" comme une fonction. Ceci est rendu possible en surchargeant le () operator. Le () operator (ne sachant pas comment il s'appelle) peut prendre n'importe quel nombre d'arguments. Les autres opérateurs n'en prennent que deux, c'est-à-dire que le + operator ne peut prendre que deux valeurs (une de chaque côté de l'opérateur) et renvoie la valeur pour laquelle vous l'avez surchargé. Vous pouvez insérer n'importe quel nombre d'arguments dans un () operator, ce qui lui donne sa souplesse.

Pour créer un foncteur, vous créez d’abord votre classe. Ensuite, vous créez un constructeur pour la classe avec un paramètre de type et de nom de votre choix. Ceci est suivi dans la même instruction par une liste d'initialiseur (qui utilise un seul opérateur du colon, quelque chose que je connaissais aussi), qui construit les objets membres de la classe avec le paramètre précédemment déclaré au constructeur. Ensuite, le () operator est surchargé. Enfin, vous déclarez les objets privés de la classe ou de la structure que vous avez créée.

Mon code (j'ai trouvé les noms de variables de jalf déroutants)

class myFunctor
{ 
    public:
        /* myFunctor is the constructor. parameterVar is the parameter passed to
           the constructor. : is the initializer list operator. myObject is the
           private member object of the myFunctor class. parameterVar is passed
           to the () operator which takes it and adds it to myObject in the
           overloaded () operator function. */
        myFunctor (int parameterVar) : myObject( parameterVar ) {}

        /* the "operator" Word is a keyword which indicates this function is an 
           overloaded operator function. The () following this just tells the
           compiler that () is the operator being overloaded. Following that is
           the parameter for the overloaded operator. This parameter is actually
           the argument "parameterVar" passed by the constructor we just wrote.
           The last part of this statement is the overloaded operators body
           which adds the parameter passed to the member object. */
        int operator() (int myArgument) { return myObject + myArgument; }

    private: 
        int myObject; //Our private member object.
}; 

Si tout ceci est inexact ou tout simplement faux, n'hésitez pas à me corriger!

35
Johanne Irish

Un foncteur est un fonction d'ordre supérieur qui applique une fonction aux types paramétrés (c.-à-d. Basés sur des modèles). C'est une généralisation de la fonction map d'ordre supérieur. Par exemple, nous pourrions définir un foncteur pour std::vector comme ceci:

template<class F, class T, class U=decltype(std::declval<F>()(std::declval<T>()))>
std::vector<U> fmap(F f, const std::vector<T>& vec)
{
    std::vector<U> result;
    std::transform(vec.begin(), vec.end(), std::back_inserter(result), f);
    return result;
}

Cette fonction prend un std::vector<T> et retourne std::vector<U> quand une fonction F prend un T et retourne un U. Un foncteur n'a pas besoin d'être défini sur les types de conteneur, il peut également l'être pour tout type basé sur un modèle, y compris std::shared_ptr:

template<class F, class T, class U=decltype(std::declval<F>()(std::declval<T>()))>
std::shared_ptr<U> fmap(F f, const std::shared_ptr<T>& p)
{
    if (p == nullptr) return nullptr;
    else return std::shared_ptr<U>(new U(f(*p)));
}

Voici un exemple simple qui convertit le type en un double:

double to_double(int x)
{
    return x;
}

std::shared_ptr<int> i(new int(3));
std::shared_ptr<double> d = fmap(to_double, i);

std::vector<int> is = { 1, 2, 3 };
std::vector<double> ds = fmap(to_double, is);

Il y a deux lois que les foncteurs devraient suivre. La première est la loi sur l’identité, qui stipule que si le foncteur est doté d’une fonction d’identité, cela doit être identique à l’application de la fonction d’identité au type, c’est-à-dire que fmap(identity, x) doit être identique à identity(x) :

struct identity_f
{
    template<class T>
    T operator()(T x) const
    {
        return x;
    }
};
identity_f identity = {};

std::vector<int> is = { 1, 2, 3 };
// These two statements should be equivalent.
// is1 should equal is2
std::vector<int> is1 = fmap(identity, is);
std::vector<int> is2 = identity(is);

La loi suivante est la loi de composition, qui stipule que si le foncteur a une composition de deux fonctions, il devrait être identique à l’application du foncteur pour la première fonction, puis de nouveau pour la deuxième fonction. Donc, fmap(std::bind(f, std::bind(g, _1)), x) devrait être identique à fmap(f, fmap(g, x)):

double to_double(int x)
{
    return x;
}

struct foo
{
    double x;
};

foo to_foo(double x)
{
    foo r;
    r.x = x;
    return r;
}

std::vector<int> is = { 1, 2, 3 };
// These two statements should be equivalent.
// is1 should equal is2
std::vector<foo> is1 = fmap(std::bind(to_foo, std::bind(to_double, _1)), is);
std::vector<foo> is2 = fmap(to_foo, fmap(to_double, is));
18
Paul Fultz II

Voici une situation réelle dans laquelle j'ai été forcé d'utiliser un foncteur pour résoudre mon problème:

J'ai un ensemble de fonctions (par exemple 20), et elles sont toutes identiques, sauf que chacune appelle une fonction spécifique différente à 3 endroits spécifiques.

C'est un gaspillage incroyable et une duplication de code. Normalement, je passerais simplement dans un pointeur de fonction et je l'appellerais simplement dans les 3 points. (Le code ne doit donc apparaître qu'une fois, au lieu de vingt fois.)

Mais ensuite, j'ai réalisé que dans chaque cas, la fonction spécifique nécessitait un profil de paramètre complètement différent! Parfois 2 paramètres, parfois 5 paramètres, etc.

Une autre solution serait d'avoir une classe de base, où la fonction spécifique est une méthode remplacée dans une classe dérivée. Mais est-ce que je veux vraiment construire tout cet héritage, juste pour pouvoir passer un pointeur de fonction ????

SOLUTION: J'ai donc créé une classe de wrapper (un "Functor") capable d'appeler n'importe laquelle des fonctions dont j'avais besoin. Je le configure à l'avance (avec ses paramètres, etc.), puis je le passe au lieu d'un pointeur de fonction. Maintenant, le code appelé peut déclencher le Functor, sans savoir ce qui se passe à l'intérieur. Il peut même l'appeler plusieurs fois (j'avais besoin d'appeler 3 fois.)


C'est ça - un exemple pratique où un Functor s'est avéré être la solution évidente et facile, ce qui m'a permis de réduire la duplication de code de 20 à 1.

9
Fellow Traveler

Excepté pour être utilisé dans callback, les foncteurs C++ peuvent aussi aider à fournir un style d'accès Matlab à une matrice classe. Il y a un exemple .

3
Yantao Xie

Comme cela a été répété, les foncteurs sont des classes qui peuvent être traitées comme des fonctions (opérateur de surcharge ()).

Ils sont particulièrement utiles pour les situations dans lesquelles vous devez associer des données à des appels répétés ou différés à une fonction.

Par exemple, une liste chaînée de foncteurs pourrait être utilisée pour implémenter un système de coroutine synchrone de base à faible surcharge, un répartiteur de tâches ou une analyse de fichier pouvant être interrompue. Exemples:

/* prints "this is a very simple and poorly used task queue" */
class Functor
{
public:
    std::string output;
    Functor(const std::string& out): output(out){}
    operator()() const
    {
        std::cout << output << " ";
    }
};

int main(int argc, char **argv)
{
    std::list<Functor> taskQueue;
    taskQueue.Push_back(Functor("this"));
    taskQueue.Push_back(Functor("is a"));
    taskQueue.Push_back(Functor("very simple"));
    taskQueue.Push_back(Functor("and poorly used"));
    taskQueue.Push_back(Functor("task queue"));
    for(std::list<Functor>::iterator it = taskQueue.begin();
        it != taskQueue.end(); ++it)
    {
        *it();
    }
    return 0;
}

/* prints the value stored in "i", then asks you if you want to increment it */
int i;
bool should_increment;
int doSomeWork()
{
    std::cout << "i = " << i << std::endl;
    std::cout << "increment? (enter the number 1 to increment, 0 otherwise" << std::endl;
    std::cin >> should_increment;
    return 2;
}
void doSensitiveWork()
{
     ++i;
     should_increment = false;
}
class BaseCoroutine
{
public:
    BaseCoroutine(int stat): status(stat), waiting(false){}
    void operator()(){ status = perform(); }
    int getStatus() const { return status; }
protected:
    int status;
    bool waiting;
    virtual int perform() = 0;
    bool await_status(BaseCoroutine& other, int stat, int change)
    {
        if(!waiting)
        {
            waiting = true;
        }
        if(other.getStatus() == stat)
        {
            status = change;
            waiting = false;
        }
        return !waiting;
    }
}

class MyCoroutine1: public BaseCoroutine
{
public:
    MyCoroutine1(BaseCoroutine& other): BaseCoroutine(1), partner(other){}
protected:
    BaseCoroutine& partner;
    virtual int perform()
    {
        if(getStatus() == 1)
            return doSomeWork();
        if(getStatus() == 2)
        {
            if(await_status(partner, 1))
                return 1;
            else if(i == 100)
                return 0;
            else
                return 2;
        }
    }
};

class MyCoroutine2: public BaseCoroutine
{
public:
    MyCoroutine2(bool& work_signal): BaseCoroutine(1), ready(work_signal) {}
protected:
    bool& work_signal;
    virtual int perform()
    {
        if(i == 100)
            return 0;
        if(work_signal)
        {
            doSensitiveWork();
            return 2;
        }
        return 1;
    }
};

int main()
{
     std::list<BaseCoroutine* > coroutineList;
     MyCoroutine2 *incrementer = new MyCoroutine2(should_increment);
     MyCoroutine1 *printer = new MyCoroutine1(incrementer);

     while(coroutineList.size())
     {
         for(std::list<BaseCoroutine *>::iterator it = coroutineList.begin();
             it != coroutineList.end(); ++it)
         {
             *it();
             if(*it.getStatus() == 0)
             {
                 coroutineList.erase(it);
             }
         }
     }
     delete printer;
     delete incrementer;
     return 0;
}

Bien sûr, ces exemples ne sont pas très utiles en eux-mêmes. Ils montrent seulement à quel point les foncteurs peuvent être utiles, les foncteurs eux-mêmes sont très basiques et inflexibles, ce qui les rend moins utiles que, par exemple, ce que boost fournit.

2
nfries88

Les foncteurs sont utilisés dans gtkmm pour connecter un bouton d'interface graphique à une fonction ou une méthode C++ réelle.


Si vous utilisez la bibliothèque pthread pour rendre votre application multithread, Functors peut vous aider.
Pour démarrer un fil, l’un des arguments de la pthread_create(..) est le pointeur de la fonction à exécuter sur son propre fil.
Mais il y a un inconvénient. Ce pointeur ne peut pas être un pointeur sur une méthode, à moins que ce soit une méthode statique, ou à moins que vous spécifiez sa classe, comme class::method. Et autre chose, l'interface de votre méthode ne peut être que:

void* method(void* something)

Donc, vous ne pouvez pas exécuter (d'une manière simple et évidente) les méthodes de votre classe dans un thread sans faire quelque chose de plus.

Un très bon moyen de gérer les threads en C++ consiste à créer votre propre classe Thread. Si vous vouliez exécuter des méthodes de la classe MyClass, ce que j'ai fait était de les transformer en classes dérivées Functor.

De plus, la classe Thread a cette méthode: static void* startThread(void* arg)
Un pointeur sur cette méthode sera utilisé comme argument pour appeler pthread_create(..). Et ce que startThread(..) devrait recevoir dans arg est une référence convertie void* à une instance dans le tas de toute classe dérivée Functor, qui sera rediffusée en Functor* lorsqu’elle sera exécutée, et alors appelée c'est la méthode run().

2
erandros

Un des grands avantages de la mise en œuvre de fonctions en tant que foncteurs est qu’ils peuvent conserver et réutiliser l’état entre les appels. Par exemple, de nombreux algorithmes de programmation dynamique, tels que algorithme de Wagner-Fischer pour calculer la distance Levenshtein entre chaînes, remplissent un grand tableau de résultats. Il est très inefficace d'allouer cette table chaque fois que la fonction est appelée. Par conséquent, l'implémentation de la fonction en tant que foncteur et le fait de faire de la table une variable membre peut améliorer considérablement les performances.

Vous trouverez ci-dessous un exemple d'application de l'algorithme de Wagner-Fischer en tant que foncteur. Remarquez comment la table est allouée dans le constructeur, puis réutilisée dans operator(), avec le redimensionnement nécessaire.

#include <string>
#include <vector>
#include <algorithm>

template <typename T>
T min3(const T& a, const T& b, const T& c)
{
   return std::min(std::min(a, b), c);
}

class levenshtein_distance 
{
    mutable std::vector<std::vector<unsigned int> > matrix_;

public:
    explicit levenshtein_distance(size_t initial_size = 8)
        : matrix_(initial_size, std::vector<unsigned int>(initial_size))
    {
    }

    unsigned int operator()(const std::string& s, const std::string& t) const
    {
        const size_t m = s.size();
        const size_t n = t.size();
        // The distance between a string and the empty string is the string's length
        if (m == 0) {
            return n;
        }
        if (n == 0) {
            return m;
        }
        // Size the matrix as necessary
        if (matrix_.size() < m + 1) {
            matrix_.resize(m + 1, matrix_[0]);
        }
        if (matrix_[0].size() < n + 1) {
            for (auto& mat : matrix_) {
                mat.resize(n + 1);
            }
        }
        // The top row and left column are prefixes that can be reached by
        // insertions and deletions alone
        unsigned int i, j;
        for (i = 1;  i <= m; ++i) {
            matrix_[i][0] = i;
        }
        for (j = 1; j <= n; ++j) {
            matrix_[0][j] = j;
        }
        // Fill in the rest of the matrix
        for (j = 1; j <= n; ++j) {
            for (i = 1; i <= m; ++i) {
                unsigned int substitution_cost = s[i - 1] == t[j - 1] ? 0 : 1;
                matrix_[i][j] =
                    min3(matrix_[i - 1][j] + 1,                 // Deletion
                    matrix_[i][j - 1] + 1,                      // Insertion
                    matrix_[i - 1][j - 1] + substitution_cost); // Substitution
            }
        }
        return matrix_[m][n];
    }
};
2
Martin Broadhurst

Pour ajouter, j'ai utilisé des objets fonction pour adapter une méthode existante au modèle de commande; (seul endroit où la beauté du OO paradigme véritable OCP que j'ai ressenti); Ajoutons également ici le modèle d’adaptateur de fonction associé.

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

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

Nous verrons comment nous pouvons l'adapter au modèle de commande. Pour cela, vous devez d'abord écrire un adaptateur de fonction membre afin qu'il puisse être appelé en tant qu'objet de fonction.

Remarque - c’est moche et vous pouvez peut-être utiliser les aides de reliure Boost, etc., mais si vous ne pouvez pas ou ne voulez pas, c’est une façon.

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

Nous avons également besoin d’une méthode d’aide mem_fun3 pour la classe ci-dessus afin de faciliter l’appel.

template<typename _Ret,typename _Class,typename _arg1,typename _arg2,typename _arg3>
mem_fun3_t<_Ret,_Class,_arg1,_arg2,_arg3> mem_fun3 ( _Ret (_Class::*_Pm)          (_arg1,_arg2,_arg3) )
{
  return (mem_fun3_t<_Ret,_Class,_arg1,_arg2,_arg3>(_Pm));

}

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

Maintenant, nous devons utiliser cela avec la classe Command; utilisez le typedef suivant:

typedef binder3<mem_fun3_t<int,T,int,int,int> ,T* ,int,int,int> F3;
//and change the signature of the ctor
//just to illustrate the usage with a method signature taking more than one parameter
explicit Command(T* pObj,F3* p_method,long timeout,const char* key,
long priority = PRIO_NORMAL ):
m_objptr(pObj),m_timeout(timeout),m_key(key),m_value(priority),method1(0),method0(0),
method(0)
{
    method3 = p_method;
}

Voici comment vous l'appelez:

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

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

Le contexte complet de ce modèle à la suivante lien

2
Alex Punnen

Functor peut également être utilisé pour simuler la définition d’une fonction locale dans une fonction. Reportez-vous à question et autre .

Mais un foncteur local ne peut pas accéder aux variables auto extérieures. La fonction lambda (C++ 11) est une meilleure solution.

1
Yantao Xie