Wat zijn C++ functors en hun gebruik?

Ik hoor steeds veel over functors in C++. Kan iemand me een overzicht geven van wat ze zijn en in welke gevallen ze nuttig zouden zijn?


Antwoord 1, autoriteit 100%

Een functor is eigenlijk gewoon een klasse die de operator() definieert. Hiermee kunt u objecten maken die “eruit zien als” een functie:

// this is a functor
struct add_x {
  add_x(int val) : x(val) {}  // Constructor
  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

Er zijn een paar leuke dingen over functors. Een daarvan is dat ze, in tegenstelling tot reguliere functies, status kunnen bevatten. In het bovenstaande voorbeeld wordt een functie gemaakt die 42 optelt bij alles wat u eraan geeft. Maar die waarde 42 is niet hardgecodeerd, het werd gespecificeerd als een constructorargument toen we onze functor-instantie maakten. Ik zou nog een opteller kunnen maken, die 27 heeft toegevoegd, gewoon door de constructor met een andere waarde aan te roepen. Dit maakt ze mooi aanpasbaar.

Zoals de laatste regels laten zien, geef je functors vaak als argumenten door aan andere functies zoals std::transform of de andere standaard bibliotheekalgoritmen. Je zou hetzelfde kunnen doen met een gewone functieaanwijzer, behalve dat, zoals ik hierboven al zei, functors kunnen worden “aangepast” omdat ze de status bevatten, waardoor ze flexibeler worden (als ik een functieaanwijzer wilde gebruiken, zou ik een functie moeten schrijven die precies 1 aan zijn argument toevoegde.De functor is algemeen en voegt toe waarmee je het hebt geïnitialiseerd), en ze zijn mogelijk ook efficiënter. In het bovenstaande voorbeeld weet de compiler precies welke functie std::transformmoet aanroepen. Het zou add_x::operator()moeten aanroepen. Dat betekent dat het die functieaanroep kan inline. En dat maakt het net zo efficiënt alsof ik de functie handmatig op elke waarde van de vector had aangeroepen.

Als ik in plaats daarvan een functieaanwijzer had doorgegeven, zou de compiler niet meteen kunnen zien naar welke functie hij verwijst, dus tenzij hij een aantal vrij complexe globale optimalisaties uitvoert, zou hij de verwijzing naar de aanwijzer tijdens runtime moeten verwijderen en vervolgens de bellen.


Antwoord 2, autoriteit 11%

Kleine toevoeging. U kunt boost::function, om functors te maken van functies en methoden, zoals dit:

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"

en je kunt boost::bind gebruiken om status aan deze functie toe te voegen

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"

en het handigst, met boost::bind en boost::functie kun je functor maken vanuit de class-methode, dit is eigenlijk een afgevaardigde:

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"

U kunt een lijst of vector van functors maken

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

Er is één probleem met al deze dingen, de foutmeldingen van de compiler zijn niet leesbaar voor mensen 🙂


Antwoord 3, autoriteit 10%

Een functor is een object dat zich gedraagt ​​als een functie.
Kortom, een klasse die operator()definieert.

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

Het echte voordeel is dat een functor de status kan vasthouden.

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)
{ ....}

Antwoord 4, autoriteit 5%

De naam “functor” wordt traditioneel gebruikt in categorietheorielang voordat C++ op het toneel verscheen . Dit heeft niets te maken met het C++-concept van functor. Het is beter om de naam function objectte gebruiken in plaats van wat we “functor” noemen in C++. Dit is hoe andere programmeertalen soortgelijke constructies noemen.

Gebruikt in plaats van gewone functie:

Kenmerken:

  • Functieobject kan status hebben
  • Functieobject past in OOP (het gedraagt ​​zich als elk ander object).

Nadelen:

  • Brengt meer complexiteit in het programma.

Gebruikt in plaats van functieaanwijzer:

Kenmerken:

  • Functieobject kan vaak inline zijn

Nadelen:

  • Functieobject kan tijdens runtime niet worden verwisseld met een ander functieobjecttype (tenminste tenzij het een basisklasse uitbreidt, wat daarom enige overhead geeft)

Gebruikt in plaats van virtuele functie:

Kenmerken:

  • Functieobject (niet-virtueel) vereist geen vtable en runtime-dispatching, dus het is in de meeste gevallen efficiënter

Nadelen:

  • Functieobject kan tijdens runtime niet worden verwisseld met een ander functieobjecttype (tenminste tenzij het een basisklasse uitbreidt, wat daarom enige overhead geeft)

Antwoord 5, autoriteit 4%

Zoals anderen al hebben gezegd, is een functor een object dat zich als een functie gedraagt, d.w.z. het overbelast de operator van de functie-aanroep.

Functors worden vaak gebruikt in STL-algoritmen. Ze zijn handig omdat ze de status voor en tussen functieaanroepen kunnen vasthouden, zoals een afsluiting in functionele talen. U kunt bijvoorbeeld een MultiplyBy-functie definiëren die het argument met een gespecificeerd bedrag vermenigvuldigt:

class MultiplyBy {
private:
    int factor;
public:
    MultiplyBy(int x) : factor(x) {
    }
    int operator () (int other) const {
        return factor * other;
    }
};

Dan zou je een MultiplyByobject kunnen doorgeven aan een algoritme zoals 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}

Een ander voordeel van een functor boven een pointer naar een functie is dat de aanroep in meer gevallen kan worden inline gezet. Als je een functieaanwijzer hebt doorgegeven aan transform, tenzij dieaanroep inline is en de compiler weet dat je er altijd dezelfde functie aan doorgeeft, kan hij de aanroep niet inline de aanwijzer.


Antwoord 6, autoriteit 3%

Voor de nieuwelingen zoals ik onder ons: na een beetje onderzoek ben ik erachter gekomen wat de code die jalf heeft gepost deed.

Een functor is een klasse- of struct-object dat als een functie kan worden “aangeroepen”. Dit wordt mogelijk gemaakt door de () operatorte overbelasten. De () operator(niet zeker hoe het heet) kan een willekeurig aantal argumenten aannemen. Andere operators nemen er maar twee, d.w.z. de + operatorkan maar twee waarden aannemen (één aan elke kant van de operator) en de waarde retourneren waarvoor je hem hebt overbelast. Je kunt een willekeurig aantal argumenten in een () operatorplaatsen, wat het zijn flexibiliteit geeft.

Als je een functor wilt maken, maak je eerst je klas aan. Vervolgens maakt u een constructor voor de klasse met een parameter van uw keuze van type en naam. Dit wordt in dezelfde instructie gevolgd door een initialisatielijst (die een enkele dubbele punt-operator gebruikt, iets waar ik ook nieuw in was) die de klasselidobjecten construeert met de eerder gedeclareerde parameter voor de constructor. Dan wordt de () operatoroverbelast. Ten slotte declareer je de privé-objecten van de klasse of struct die je hebt gemaakt.

Mijn code (ik vond de variabelenamen van jalf verwarrend)

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

Als iets hiervan onnauwkeurig of gewoon verkeerd is, corrigeer me dan gerust!


Antwoord 7, autoriteit 2%

Een functor is een hogere-orde functiedie een functie toepast op de geparametriseerde( dwz sjablonen) typen. Het is een generalisatie van de kaarthogere-orde functie. We zouden bijvoorbeeld een functor voor std::vectorals volgt kunnen definiëren:

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

Deze functie heeft een std::vector<T>en retourneert std::vector<U>wanneer een functie Fwordt gegeven die neemt een Ten retourneert een U. Een functor hoeft niet te worden gedefinieerd over containertypen, het kan ook worden gedefinieerd voor elk sjabloontype, inclusief 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)));
}

Hier is een eenvoudig voorbeeld dat het type converteert naar een 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);

Er zijn twee wetten die functors moeten volgen. De eerste is de identiteitswet, die stelt dat als de functor een identiteitsfunctie krijgt, dit hetzelfde moet zijn als het toepassen van de identiteitsfunctie op het type, dat wil zeggen fmap(identity, x)zou moeten zijn hetzelfde als 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);

De volgende wet is de samenstellingswet, die stelt dat als de functor een samenstelling van twee functies krijgt, dit hetzelfde moet zijn als het toepassen van de functor voor de eerste functie en dan weer voor de tweede functie. Dus fmap(std::bind(f, std::bind(g, _1)), x)moet hetzelfde zijn als 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));

Antwoord 8

Hier is een actuele situatie waarin ik een Functor moest gebruiken om mijn probleem op te lossen:

Ik heb een reeks functies (zeg, 20 van hen), en ze zijn allemaal identiek, behalve dat ze elk een andere specifieke functie aanroepen op 3 specifieke plekken.

Dit is ongelooflijke verspilling en codeduplicatie. Normaal gesproken zou ik gewoon een functieaanwijzer doorgeven en dat gewoon op de 3 plekken noemen. (De code hoeft dus maar één keer te verschijnen, in plaats van twintig keer.)

Maar toen realiseerde ik me dat de specifieke functie in elk geval een heel ander parameterprofiel vereiste! Soms 2 parameters, soms 5 parameters, enz.

Een andere oplossing zou zijn om een ​​basisklasse te hebben, waarbij de specifieke functie een overschreven methode is in een afgeleide klasse. Maar wil ik echt al deze INHERITANCE bouwen, zodat ik een functieaanwijzer kan doorgeven????

OPLOSSING: Dus wat ik deed was, ik maakte een wrapper-klasse (een “Functor”) die in staat is om alle functies aan te roepen die ik nodig had. Ik heb het van tevoren ingesteld (met zijn parameters, enz.) En dan geef ik het door in plaats van een functieaanwijzer. Nu kan de opgeroepen code de Functor activeren, zonder te weten wat er aan de binnenkant gebeurt. Het kan het zelfs meerdere keren bellen (ik had het nodig om 3 keer te bellen.)


Dat is het — een praktisch voorbeeld waarbij een Functor de voor de hand liggende en gemakkelijke oplossing bleek te zijn, waardoor ik codeduplicatie kon terugbrengen van 20 functies naar 1.


Antwoord 9

Functoren worden in gtkmm gebruikt om een ​​GUI-knop te verbinden met een daadwerkelijke C++-functie of -methode.


Als je de pthread-bibliotheek gebruikt om je app multithreaded te maken, kan Functors je helpen.
Om een ​​thread te starten, is een van de argumenten van de pthread_create(..)de functieaanwijzer die moet worden uitgevoerd op zijn eigen thread.
Maar er is één ongemak. Deze aanwijzer kan geen aanwijzer zijn naar een methode, tenzij het een statische methodeis, of tenzij je de klasse opgeeft, zoals class::method. En nog iets, de interface van uw methode kan alleen zijn:

void* method(void* something)

Je kunt dus niet (op een eenvoudige, voor de hand liggende manier) methoden uit je klas in een thread uitvoeren zonder iets extra’s te doen.

Een zeer goede manier om met threads in C++ om te gaan, is door uw eigen klasse Threadte maken. Als je methoden uit de klasse MyClasswilde gebruiken, heb ik die methoden getransformeerd in van Functorafgeleide klassen.

Ook heeft de klasse Threaddeze methode:
static void* startThread(void* arg)
Een verwijzing naar deze methode wordt gebruikt als argument om pthread_create(..)aan te roepen. En wat startThread(..)zou moeten ontvangen in arg is een void*gegoten verwijzing naar een instantie in de hoop van een van een van Functorafgeleide klasse, die wordt teruggestuurd naar Functor*wanneer het wordt uitgevoerd, en wordt vervolgens de methode run()genoemd.


Antwoord 10

Behalve voor gebruik bij terugbellen, kunnen C++ functors ook helpen om een ​​Matlabtoegangsstijl voor een matrixklasse te geven. Er is een voorbeeld.


Antwoord 11

Zoals is herhaald, functors zijn klassen die als functies kunnen worden behandeld (operator voor overbelasting ()).

Ze zijn vooral handig voor situaties waarin u bepaalde gegevens moet koppelen aan herhaalde of vertraagde aanroepen van een functie.

Een gekoppelde lijst met functors kan bijvoorbeeld worden gebruikt om een ​​basaal synchroon coroutinesysteem met lage overheadkosten, een taakdispatcher of onderbreekbare bestandsparsering te implementeren.
Voorbeelden:

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

Natuurlijk zijn deze voorbeelden op zich niet zo handig. Ze laten alleen zien hoe functors nuttig kunnen zijn, de functors zelf zijn erg basaal en inflexibel en dit maakt ze minder bruikbaar dan bijvoorbeeld wat boost biedt.


Antwoord 12

Een groot voordeel van het implementeren van functies als functors is dat ze de status tussen aanroepen kunnen behouden en hergebruiken. Veel dynamische programmeeralgoritmen, zoals het Wagner-Fischer-algoritmevoor het berekenen van de Levenshtein-afstandtussen strings, vul je een grote tabel met resultaten in. Het is erg inefficiënt om deze tabel elke keer dat de functie wordt aangeroepen toe te wijzen, dus het implementeren van de functie als functor en het maken van een lidvariabele van de tabel kan de prestaties aanzienlijk verbeteren.

Hieronder ziet u een voorbeeld van het implementeren van het Wagner-Fischer-algoritme als functor. Merk op hoe de tabel wordt toegewezen in de constructor en vervolgens opnieuw wordt gebruikt in operator(), met indien nodig het formaat wijzigen.

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

Antwoord 13

Als aanvulling heb ik functie-objecten gebruikt om een ​​bestaande legacy-methode aan te passen aan het opdrachtpatroon; (enige plek waar ik de schoonheid van het OO-paradigma waar OCP voelde); Voeg hier ook het gerelateerde functie-adapterpatroon toe.

Stel dat uw methode de handtekening heeft:

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

We zullen zien hoe we het kunnen aanpassen aan het Command-patroon – hiervoor moet je eerst een lidfunctie-adapter schrijven zodat het als een functie-object kan worden aangeroepen.

Opmerking – dit is lelijk en misschien kun je de Boost-bindhelpers enz. gebruiken, maar als je dat niet kunt of wilt, is dit een manier.

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

We hebben ook een hulpmethode mem_fun3nodig voor de bovenstaande klasse om te helpen bij het aanroepen.

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

Om de parameters te binden, moeten we een binderfunctie schrijven. Dus hier gaat het:

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

En een hulpfunctie om de binder3-klasse te gebruiken – 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);
}

Nu moeten we dit gebruiken met de klasse Command; gebruik het volgende typedef:

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

Zo noem je het:

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

Opmerking: f3();roept de methode task1->ThreeParameterTask(21,22,23);aan.

De volledige context van dit patroon op de volgende link


Antwoord 14

Functor kan ook worden gebruikt om het definiëren van een lokale functie binnen een functie te simuleren. Raadpleeg de vraagen een andere.

Maar een lokale functor heeft geen toegang tot externe automatische variabelen. De lambda-functie (C++11) is een betere oplossing.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Other episodes