webentwicklung-frage-antwort-db.com.de

Was sind C ++ - Funktoren und deren Verwendung?

Ich höre immer wieder viel über Funktoren in C++. Kann mir jemand einen Überblick geben, was sie sind und in welchen Fällen sie nützlich wären?

811
Konrad

Ein Funktor ist so ziemlich nur eine Klasse, die den Operator () definiert. So können Sie Objekte erstellen, die wie eine Funktion aussehen:

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

Es gibt ein paar nette Dinge über Funktoren. Zum einen können sie im Gegensatz zu regulären Funktionen state enthalten. Das obige Beispiel erstellt eine Funktion, die 42 zu dem hinzufügt, was Sie ihr geben. Dieser Wert 42 ist jedoch nicht fest codiert. Er wurde als Konstruktorargument angegeben, als wir unsere Funktionsinstanz erstellt haben. Ich könnte einen weiteren Addierer erstellen, der 27 addiert, indem ich den Konstruktor mit einem anderen Wert aufrufe. Dies macht sie schön anpassbar.

Wie die letzten Zeilen zeigen, übergeben Sie häufig Funktoren als Argumente an andere Funktionen wie std :: transform oder die anderen Standardbibliotheksalgorithmen. Sie können dasselbe mit einem normalen Funktionszeiger tun, außer dass Funktoren, wie oben erwähnt, "angepasst" werden können, da sie den Status enthalten, wodurch sie flexibler werden (Wenn ich einen Funktionszeiger verwenden wollte, müsste ich eine Funktion schreiben Der Funktor ist allgemein und fügt alles hinzu, womit Sie ihn initialisiert haben.) Außerdem sind sie potenziell effizienter. Im obigen Beispiel weiß der Compiler genau, welche Funktion std::transform aufrufen soll. Es sollte add_x::operator() aufrufen. Dies bedeutet, dass dieser Funktionsaufruf eingebunden werden kann. Und das macht es genauso effizient, als hätte ich die Funktion für jeden Wert des Vektors manuell aufgerufen.

Wenn ich stattdessen einen Funktionszeiger übergeben hätte, konnte der Compiler nicht sofort erkennen, auf welche Funktion er verweist. Wenn er also keine recht komplexen globalen Optimierungen durchführt, müsste er den Zeiger zur Laufzeit dereferenzieren und dann den Aufruf ausführen.

975
jalf

Kleiner Zusatz. Sie können boost::function verwenden, um Funktoren aus Funktionen und Methoden wie folgt zu erstellen:

_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"
_

und Sie können boost :: bind verwenden, um diesem Funktor einen Status hinzuzufügen

_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"
_

und am nützlichsten ist, dass Sie mit den Funktionen boost :: bind und boost :: functor aus der Klassenmethode erstellen können. Eigentlich ist dies ein Delegat:

_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"
_

Sie können eine Liste oder einen Vektor von Funktoren erstellen

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

Es gibt ein Problem mit all dem Zeug, Compiler-Fehlermeldungen sind nicht lesbar :)

118
Evgeny Lazin

Ein Functor ist ein Objekt, das sich wie eine Funktion verhält. Grundsätzlich eine Klasse, die operator() definiert.

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

MyFunctor doubler;
int x = doubler(5);

Der wirkliche Vorteil ist, dass ein Funktor den Zustand halten kann.

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

Der Name "functor" wurde traditionell in Kategorietheorie verwendet, lange bevor C++ in der Szene auftauchte. Dies hat nichts mit dem C++ - Konzept von functor zu tun. Es ist besser, das Funktionsobjekt name anstelle dessen zu verwenden, was wir in C++ "functor" nennen. So nennen andere Programmiersprachen ähnliche Konstrukte.

Wird anstelle der normalen Funktion verwendet:

Eigenschaften:

  • Funktionsobjekt kann Zustand haben
  • Funktionsobjekt passt in OOP (es verhält sich wie jedes andere Objekt).

Nachteile:

  • Verleiht dem Programm mehr Komplexität.

Wird anstelle des Funktionszeigers verwendet:

Eigenschaften:

  • Funktionsobjekt kann häufig inline sein

Nachteile:

  • Das Funktionsobjekt kann zur Laufzeit nicht mit einem anderen Funktionsobjekttyp ausgetauscht werden (zumindest, wenn es keine Basisklasse erweitert, was zu einem gewissen Overhead führt).

Wird anstelle der virtuellen Funktion verwendet:

Eigenschaften:

  • Funktionsobjekt (nicht virtuell) erfordert kein vtable- und Runtime-Dispatching und ist daher in den meisten Fällen effizienter

Nachteile:

  • Das Funktionsobjekt kann zur Laufzeit nicht mit einem anderen Funktionsobjekttyp ausgetauscht werden (zumindest, wenn es keine Basisklasse erweitert, was zu einem gewissen Overhead führt).
49
doc

Wie andere erwähnt haben, ist ein Funktor ein Objekt, das sich wie eine Funktion verhält, d. H. Es überlastet den Funktionsaufrufoperator.

Funktoren werden häufig in STL-Algorithmen verwendet. Sie sind nützlich, weil sie den Status vor und zwischen Funktionsaufrufen beibehalten können, wie z. B. ein Abschluss in funktionalen Sprachen. Beispielsweise könnten Sie einen MultiplyBy -Funktor definieren, der sein Argument mit einem bestimmten Betrag multipliziert:

class MultiplyBy {
private:
    int factor;

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

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

Dann könnten Sie ein MultiplyBy -Objekt an einen Algorithmus wie std :: transform übergeben:

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

Ein weiterer Vorteil eines Funktors gegenüber einem Zeiger auf eine Funktion besteht darin, dass der Aufruf in mehreren Fällen eingebunden werden kann. Wenn Sie einen Funktionszeiger an transform übergeben haben, kann der Aufruf nicht über den Zeiger eingebunden werden, es sei denn, dass der Aufruf wurde eingebunden und der Compiler weiß, dass Sie ihm immer dieselbe Funktion übergeben.

38
Matthew Crumley

Für die Neulinge wie mich unter uns: Nach ein wenig Recherche habe ich herausgefunden, was der von jalf gepostete Code macht.

Ein Funktor ist ein Klassen- oder Strukturobjekt, das wie eine Funktion "aufgerufen" werden kann. Dies wird durch Überladen des () operator ermöglicht. Der () operator (nicht sicher, wie er heißt) kann eine beliebige Anzahl von Argumenten annehmen. Andere Operatoren nehmen nur zwei, d. H. Der + operator kann nur zwei Werte annehmen (einen auf jeder Seite des Operators) und den Wert zurückgeben, für den Sie ihn überladen haben. Sie können eine beliebige Anzahl von Argumenten in einen () operator einfügen, was ihm seine Flexibilität verleiht.

Um zuerst einen Funktor zu erstellen, erstellen Sie Ihre Klasse. Anschließend erstellen Sie einen Konstruktor für die Klasse mit einem Parameter Ihrer Wahl von Typ und Name. Darauf folgt in derselben Anweisung eine Initialisierungsliste (die einen einzelnen Doppelpunktoperator verwendet, was mir ebenfalls neu war), die die Klassenelementobjekte mit dem zuvor deklarierten Parameter für den Konstruktor erstellt. Dann ist der () operator überladen. Schließlich deklarieren Sie die privaten Objekte der von Ihnen erstellten Klasse oder Struktur.

Mein Code (ich fand Jalfs Variablennamen verwirrend)

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

Wenn irgendetwas davon ungenau oder einfach falsch ist, zögern Sie nicht, mich zu korrigieren!

35
Johanne Irish

Ein Funktor ist eine Funktion höherer Ordnung , die eine Funktion auf die parametrisierten (dh Vorlagen-) Typen anwendet. Es ist eine Verallgemeinerung der Funktion map höherer Ordnung. Zum Beispiel könnten wir einen Funktor für std::vector wie folgt definieren:

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

Diese Funktion nimmt einen std::vector<T> und gibt std::vector<U> zurück, wenn eine Funktion F gegeben wird, die einen T annimmt und einen U zurückgibt. Ein Funktor muss nicht über Containertypen definiert werden, er kann auch für jeden Typ mit Vorlagen definiert werden, einschließlich 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 ist ein einfaches Beispiel, das den Typ in ein double konvertiert:

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

Es gibt zwei Gesetze, denen Funktoren folgen sollten. Das erste ist das Identitätsgesetz, das besagt, dass, wenn dem Funktor eine Identitätsfunktion zugewiesen wird, dies dasselbe sein sollte wie das Anwenden der Identitätsfunktion auf den Typ, das heißt, dass fmap(identity, x) dasselbe sein sollte wie 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);

Das nächste Gesetz ist das Kompositionsgesetz, das besagt, dass, wenn dem Funktor eine Komposition aus zwei Funktionen gegeben wird, dies dasselbe sein sollte, als würde der Funktor für die erste Funktion und dann erneut für die zweite Funktion angewendet. Also sollte fmap(std::bind(f, std::bind(g, _1)), x) dasselbe sein wie 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

Hier ist eine aktuelle Situation, in der ich gezwungen war, einen Functor zu verwenden, um mein Problem zu lösen:

Ich habe eine Reihe von Funktionen (z. B. 20), und sie sind alle identisch, mit der Ausnahme, dass jede an drei bestimmten Stellen eine andere spezifische Funktion aufruft.

Das ist unglaubliche Verschwendung und Vervielfältigung von Code. Normalerweise würde ich einfach einen Funktionszeiger übergeben und das an den 3 Stellen aufrufen. (Der Code muss also nicht zwanzig Mal, sondern nur einmal vorkommen.)

Dann wurde mir aber klar, dass für die jeweilige Funktion ein völlig anderes Parameterprofil erforderlich ist! Manchmal 2 Parameter, manchmal 5 Parameter usw.

Eine andere Lösung wäre eine Basisklasse, bei der die spezifische Funktion eine überschriebene Methode in einer abgeleiteten Klasse ist. Aber möchte ich wirklich all diese Vererbung aufbauen, nur damit ich einen Funktionszeiger übergeben kann?

LÖSUNG: Also habe ich eine Wrapper-Klasse (einen "Functor") erstellt, mit der ich alle Funktionen aufrufen kann, die ich benötigt habe. Ich richte es im Voraus ein (mit seinen Parametern usw.) und übergebe es dann anstelle eines Funktionszeigers. Jetzt kann der aufgerufene Code den Functor auslösen, ohne zu wissen, was im Inneren passiert. Es kann sogar mehrmals aufgerufen werden (ich brauchte es, um 3 mal anzurufen.)


Das war's - ein praktisches Beispiel, bei dem sich ein Functor als offensichtliche und einfache Lösung herausstellte, mit der ich die Code-Duplikation von 20 Funktionen auf 1 reduzieren konnte.

9
Fellow Traveler

Mit Ausnahme der Verwendung in Callback können C++ - Funktoren auch dabei helfen, einen Matlab -Zugriffsstil für eine - Matrixklasse bereitzustellen. Es gibt ein Beispiel .

3
Yantao Xie

Wie bereits wiederholt, sind Funktoren Klassen, die als Funktionen behandelt werden können (Überladungsoperator ()).

Sie eignen sich am besten für Situationen, in denen Sie einige Daten mit wiederholten oder verzögerten Aufrufen einer Funktion verknüpfen müssen.

Beispielsweise könnte eine verknüpfte Liste von Funktoren verwendet werden, um ein grundlegendes synchrones Coroutine-System mit geringem Overhead, einen Task-Dispatcher oder eine unterbrechbare Dateiparsing zu implementieren. Beispiele:

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

Natürlich sind diese Beispiele an sich nicht so nützlich. Sie zeigen nur, wie nützlich Funktoren sein können, die Funktoren selbst sind sehr einfach und unflexibel, und dies macht sie weniger nützlich als zum Beispiel, was Boost bietet.

2
nfries88

Functors werden in gtkmm verwendet, um eine GUI-Schaltfläche mit einer tatsächlichen C++ - Funktion oder -Methode zu verbinden.


Wenn Sie die pthread-Bibliothek verwenden, um Ihre App multithreadingfähig zu machen, kann Functors Ihnen helfen.
Um einen Thread zu starten, ist eines der Argumente von pthread_create(..) der Funktionszeiger, der in seinem eigenen Thread ausgeführt werden soll.
Aber es gibt eine Unannehmlichkeit. Dieser Zeiger kann kein Zeiger auf eine Methode sein, es sei denn, es ist eine statische Methode oder Sie geben die Klasse an, wie class::method. Und eine andere Sache, die Schnittstelle Ihrer Methode kann nur sein:

void* method(void* something)

Sie können also nicht (auf einfache, offensichtliche Weise) Methoden aus Ihrer Klasse in einem Thread ausführen, ohne etwas Besonderes zu tun.

Eine sehr gute Möglichkeit, mit Threads in C++ umzugehen, besteht darin, eine eigene Thread -Klasse zu erstellen. Wenn Sie Methoden aus der Klasse MyClass ausführen wollten, haben Sie diese Methoden in von Functor abgeleitete Klassen umgewandelt.

Die Klasse Thread verfügt außerdem über die folgende Methode: static void* startThread(void* arg)
Ein Zeiger auf diese Methode wird als Argument verwendet, um pthread_create(..) aufzurufen. Und was startThread(..) in arg erhalten soll, ist ein void* geworfener Verweis auf eine Instanz in einem Heap einer Functor abgeleiteten Klasse, die bei Ausführung auf Functor* zurückgeworfen wird, und dann heißt es run() Methode.

2
erandros

Ein großer Vorteil der Implementierung von Funktionen als Funktoren besteht darin, dass sie den Status zwischen Aufrufen beibehalten und wiederverwenden können. Beispielsweise arbeiten viele dynamische Programmieralgorithmen, wie der Wagner-Fischer-Algorithmus zur Berechnung des Levenshtein-Abstands zwischen Zeichenfolgen, indem sie eine große Ergebnistabelle ausfüllen. Es ist sehr ineffizient, diese Tabelle bei jedem Funktionsaufruf zuzuweisen, sodass die Implementierung der Funktion als Funktor und die Umwandlung der Tabelle in eine Mitgliedsvariable die Leistung erheblich verbessern kann.

Nachfolgend finden Sie ein Beispiel für die Implementierung des Wagner-Fischer-Algorithmus als Funktor. Beachten Sie, wie die Tabelle im Konstruktor zugewiesen und dann in operator() wiederverwendet wird, wobei die Größe nach Bedarf geändert wird.

#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

Zum Hinzufügen habe ich Funktionsobjekte verwendet, um eine vorhandene Legacy-Methode an das Befehlsmuster anzupassen. (Nur dort, wo ich die Schönheit des OO Paradigmas wahrer OCP empfand); Fügen Sie hier auch das zugehörige Funktionsadaptermuster hinzu.

Angenommen, Ihre Methode hat die Signatur:

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

Wir werden sehen, wie wir es für das Befehlsmuster anpassen können. Dazu müssen Sie zuerst einen Mitgliedsfunktionsadapter schreiben, damit er als Funktionsobjekt aufgerufen werden kann.

Hinweis - Dies ist hässlich und möglicherweise können Sie die Boost-Bindehilfen usw. verwenden. Wenn Sie dies jedoch nicht können oder wollen, ist dies eine Möglichkeit.

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

Außerdem benötigen wir eine Hilfsmethode mem_fun3 für die oben genannte Klasse, um den Aufruf zu unterstützen.

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

}

Um die Parameter zu binden, müssen wir nun eine Bindefunktion schreiben. Also, hier geht es:

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

Und eine Hilfsfunktion zur Verwendung der binder3-Klasse - 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);
}

Jetzt müssen wir dies mit der Command-Klasse verwenden; Verwenden Sie die folgende 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;
}

So nennst du es:

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

Anmerkung: f3 (); ruft die Methode task1-> ThreeParameterTask (21,22,23) auf.

Den vollständigen Kontext dieses Musters finden Sie unter link

2
Alex Punnen

Mit Functor kann auch simuliert werden, wie eine lokale Funktion innerhalb einer Funktion definiert wird. Lesen Sie die Frage und andere .

Ein lokaler Funktor kann jedoch nicht auf externe Auto-Variablen zugreifen. Die Lambda (C++ 11) -Funktion ist eine bessere Lösung.

1
Yantao Xie